contextos-agents 1.6.1 → 1.7.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/AGENTS.md +5 -0
- package/.agents/adapters/aider/export.js +4 -2
- package/.agents/adapters/copilot/export.js +4 -2
- package/.agents/adapters/cursor/export.js +48 -12
- package/.agents/adapters/zed/export.js +53 -14
- package/.agents/core/skills/adapters/skill.yaml +6 -0
- package/.agents/core/skills/architecture-diagrams/skill.yaml +4 -0
- package/.agents/core/skills/brutalist-design/skill.yaml +4 -0
- package/.agents/core/skills/context-manager/references/context-rules.md +59 -0
- package/.agents/core/skills/context-manager/skill.yaml +7 -0
- package/.agents/core/skills/context-os/skill.yaml +11 -0
- package/.agents/core/skills/database/skill.yaml +6 -0
- package/.agents/core/skills/ddd/skill.yaml +7 -0
- package/.agents/core/skills/decisions/skill.yaml +6 -0
- package/.agents/core/skills/docker/skill.yaml +6 -0
- package/.agents/core/skills/engineering-workflow/SKILL.md +1 -1
- package/.agents/core/skills/engineering-workflow/skill.yaml +6 -0
- package/.agents/core/skills/fastapi/skill.yaml +7 -0
- package/.agents/core/skills/gemini-precision/SKILL.md +4 -0
- package/.agents/core/skills/gemini-precision/skill.yaml +4 -0
- package/.agents/core/skills/generators/skill.yaml +15 -0
- package/.agents/core/skills/graphify/SKILL.md +2 -2
- package/.agents/core/skills/graphify/skill.yaml +6 -1
- package/.agents/core/skills/gstack-roles/SKILL.md +3 -1
- package/.agents/core/skills/gstack-roles/skill.yaml +6 -0
- package/.agents/core/skills/impeccable-design/skill.yaml +6 -0
- package/.agents/core/skills/interview-me/skill.yaml +4 -0
- package/.agents/core/skills/microservices/skill.yaml +7 -0
- package/.agents/core/skills/minimalist-design/skill.yaml +4 -0
- package/.agents/core/skills/nestjs/skill.yaml +7 -0
- package/.agents/core/skills/nextjs/skill.yaml +7 -0
- package/.agents/core/skills/node/skill.yaml +7 -0
- package/.agents/core/skills/performance/skill.yaml +7 -0
- package/.agents/core/skills/ponytail-mindset/skill.yaml +6 -0
- package/.agents/core/skills/react/skill.yaml +7 -0
- package/.agents/core/skills/react-best-practices/skill.yaml +4 -0
- package/.agents/core/skills/redesign-audit/skill.yaml +4 -0
- package/.agents/core/skills/security/skill.yaml +7 -0
- package/.agents/core/skills/soft-design/skill.yaml +4 -0
- package/.agents/core/skills/state-management/skill.yaml +6 -0
- package/.agents/core/skills/subagent-orchestrator/SKILL.md +17 -0
- package/.agents/core/skills/subagent-orchestrator/skill.yaml +4 -0
- package/.agents/core/skills/system-design/skill.yaml +7 -0
- package/.agents/core/skills/testing/skill.yaml +6 -0
- package/.agents/core/skills/typescript/skill.yaml +7 -0
- package/.agents/core/skills/ui-design/skill.yaml +7 -0
- package/.agents/core/skills/ui-ux-pro/skill.yaml +6 -0
- package/.agents/core/skills/ux-design/skill.yaml +7 -0
- package/.agents/core/skills/vercel-optimize/scripts/collect-signals.mjs +131 -0
- package/.agents/core/skills/vercel-optimize/scripts/gate-investigations.mjs +142 -0
- package/.agents/core/skills/vercel-optimize/scripts/merge-signals.mjs +143 -0
- package/.agents/core/skills/vercel-optimize/scripts/scan-codebase.mjs +174 -0
- package/.agents/core/skills/vercel-optimize/skill.yaml +8 -0
- package/.agents/core/skills/web-accessibility/skill.yaml +7 -0
- package/.agents/ctx.js +14 -1
- package/.agents/doctor.js +193 -4
- package/.agents/generated/claude/skills/engineering-workflow/SKILL.md +1 -1
- package/.agents/generated/claude/skills/gemini-precision/SKILL.md +4 -0
- package/.agents/generated/claude/skills/graphify/SKILL.md +1 -1
- package/.agents/generated/claude/skills/gstack-roles/SKILL.md +3 -1
- package/.agents/generated/claude/skills/subagent-orchestrator/SKILL.md +17 -0
- package/.agents/generated/gemini/skills/engineering-workflow/SKILL.md +1 -1
- package/.agents/generated/gemini/skills/gemini-precision/SKILL.md +4 -0
- package/.agents/generated/gemini/skills/graphify/SKILL.md +2 -2
- package/.agents/generated/gemini/skills/gstack-roles/SKILL.md +3 -1
- package/.agents/generated/gemini/skills/subagent-orchestrator/SKILL.md +17 -0
- package/.agents/plugins/contextos/hooks.json +25 -0
- package/.agents/plugins/contextos/plugin.json +19 -0
- package/.agents/plugins.js +180 -20
- package/.agents/profiles.js +26 -0
- package/.agents/resolver.js +150 -30
- package/.agents/stats.js +22 -9
- package/.agents/validate.js +99 -13
- package/bin/commands/uninstall.js +207 -0
- package/bin/commands/update.js +262 -0
- package/bin/commands.js +305 -0
- package/bin/index.js +203 -83
- package/bin/lib/detector.js +106 -0
- package/bin/lib/lockfile.js +251 -0
- package/bin/lib/safe-writer.js +259 -0
- package/package.json +5 -6
- package/registry.json +13 -5
- package/registry.schema.json +3 -1
- package/.agents/mcp/runtime.py +0 -470
- package/.agents/mcp/server.mjs +0 -189271
- package/benchmarks/gemini-issues.js +0 -533
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* merge-signals.mjs
|
|
4
|
+
* Merges Vercel telemetry production metrics with codebase route scan outputs.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* node scripts/merge-signals.mjs <signals-file> <codebase-file> [--out <file>] [--help]
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { readFileSync, writeFileSync, existsSync } from 'node:fs';
|
|
11
|
+
import { resolve } from 'node:path';
|
|
12
|
+
|
|
13
|
+
function printHelp() {
|
|
14
|
+
console.log(`
|
|
15
|
+
merge-signals.mjs — Correlates Telemetry Metrics with Codebase Routes
|
|
16
|
+
|
|
17
|
+
Usage:
|
|
18
|
+
node scripts/merge-signals.mjs <signals-file> <codebase-file> [options]
|
|
19
|
+
|
|
20
|
+
Arguments:
|
|
21
|
+
signals-file JSON file generated by collect-signals.mjs
|
|
22
|
+
codebase-file JSON file generated by scan-codebase.mjs
|
|
23
|
+
|
|
24
|
+
Options:
|
|
25
|
+
--out <file> Output destination file (default: stdout)
|
|
26
|
+
--help, -h Show this help message
|
|
27
|
+
`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const args = process.argv.slice(2);
|
|
31
|
+
if (args.includes('--help') || args.includes('-h') || args.length < 2) {
|
|
32
|
+
printHelp();
|
|
33
|
+
process.exit(args.includes('--help') || args.includes('-h') ? 0 : 1);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const positional = [];
|
|
37
|
+
let outFile = null;
|
|
38
|
+
|
|
39
|
+
for (let i = 0; i < args.length; i++) {
|
|
40
|
+
const arg = args[i];
|
|
41
|
+
if (arg === '--out' && i + 1 < args.length) {
|
|
42
|
+
outFile = args[++i];
|
|
43
|
+
} else if (!arg.startsWith('-')) {
|
|
44
|
+
positional.push(arg);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (positional.length < 2) {
|
|
49
|
+
console.error('Error: Both <signals-file> and <codebase-file> are required.');
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const [signalsPath, codebasePath] = positional;
|
|
54
|
+
if (!existsSync(signalsPath)) {
|
|
55
|
+
console.error(`Error: File not found: ${signalsPath}`);
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
if (!existsSync(codebasePath)) {
|
|
59
|
+
console.error(`Error: File not found: ${codebasePath}`);
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
let signalsData = {};
|
|
64
|
+
let codebaseData = {};
|
|
65
|
+
|
|
66
|
+
try {
|
|
67
|
+
signalsData = JSON.parse(readFileSync(resolve(process.cwd(), signalsPath), 'utf8'));
|
|
68
|
+
} catch (e) {
|
|
69
|
+
console.error(`Error parsing signals JSON: ${e.message}`);
|
|
70
|
+
process.exit(1);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
try {
|
|
74
|
+
codebaseData = JSON.parse(readFileSync(resolve(process.cwd(), codebasePath), 'utf8'));
|
|
75
|
+
} catch (e) {
|
|
76
|
+
console.error(`Error parsing codebase JSON: ${e.message}`);
|
|
77
|
+
process.exit(1);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const telemetryRoutes = signalsData.routes || [];
|
|
81
|
+
const codeRoutes = codebaseData.routes || [];
|
|
82
|
+
|
|
83
|
+
// Correlate metrics with source files
|
|
84
|
+
const mergedRoutes = [];
|
|
85
|
+
|
|
86
|
+
for (const tr of telemetryRoutes) {
|
|
87
|
+
// Find matching route in codebase
|
|
88
|
+
const match = codeRoutes.find(cr => {
|
|
89
|
+
return cr.route === tr.route || cr.route === tr.route + '/' || tr.route === cr.route + '/';
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
mergedRoutes.push({
|
|
93
|
+
...tr,
|
|
94
|
+
file: match ? match.file : null,
|
|
95
|
+
frameworkConfig: match ? {
|
|
96
|
+
type: match.type,
|
|
97
|
+
dynamic: match.dynamic,
|
|
98
|
+
revalidate: match.revalidate,
|
|
99
|
+
runtime: match.runtime
|
|
100
|
+
} : null
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Add codebase routes that had no telemetry records
|
|
105
|
+
for (const cr of codeRoutes) {
|
|
106
|
+
const existing = mergedRoutes.some(mr => mr.file === cr.file || mr.route === cr.route);
|
|
107
|
+
if (!existing) {
|
|
108
|
+
mergedRoutes.push({
|
|
109
|
+
route: cr.route,
|
|
110
|
+
method: 'GET',
|
|
111
|
+
invocations: 0,
|
|
112
|
+
p95DurationMs: 0,
|
|
113
|
+
avgDurationMs: 0,
|
|
114
|
+
coldStartRate: 0,
|
|
115
|
+
errorRate: 0,
|
|
116
|
+
cacheable: cr.dynamic === 'force-static' || cr.revalidate != null,
|
|
117
|
+
file: cr.file,
|
|
118
|
+
frameworkConfig: {
|
|
119
|
+
type: cr.type,
|
|
120
|
+
dynamic: cr.dynamic,
|
|
121
|
+
revalidate: cr.revalidate,
|
|
122
|
+
runtime: cr.runtime
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const merged = {
|
|
129
|
+
timestamp: new Date().toISOString(),
|
|
130
|
+
projectId: signalsData.projectId || 'unknown',
|
|
131
|
+
windowDays: signalsData.windowDays || 14,
|
|
132
|
+
framework: codebaseData.framework || 'unknown',
|
|
133
|
+
metrics: signalsData.metrics || {},
|
|
134
|
+
middleware: codebaseData.middleware || [],
|
|
135
|
+
routes: mergedRoutes
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
const outputJson = JSON.stringify(merged, null, 2);
|
|
139
|
+
if (outFile) {
|
|
140
|
+
writeFileSync(resolve(process.cwd(), outFile), outputJson, 'utf8');
|
|
141
|
+
} else {
|
|
142
|
+
console.log(outputJson);
|
|
143
|
+
}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* scan-codebase.mjs
|
|
4
|
+
* Scans repository routes, middleware, and rendering directives for Vercel optimization.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* node scripts/scan-codebase.mjs <repo-root> [--out <file>] [--help]
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { readdirSync, statSync, readFileSync, writeFileSync, existsSync } from 'node:fs';
|
|
11
|
+
import { resolve, join, relative, basename } from 'node:path';
|
|
12
|
+
|
|
13
|
+
function printHelp() {
|
|
14
|
+
console.log(`
|
|
15
|
+
scan-codebase.mjs — Vercel Codebase Route & Config Scanner
|
|
16
|
+
|
|
17
|
+
Usage:
|
|
18
|
+
node scripts/scan-codebase.mjs <repo-root> [options]
|
|
19
|
+
|
|
20
|
+
Arguments:
|
|
21
|
+
repo-root Path to the repository root directory to scan
|
|
22
|
+
|
|
23
|
+
Options:
|
|
24
|
+
--out <file> Output file path (default: stdout)
|
|
25
|
+
--help, -h Show this help message
|
|
26
|
+
`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const args = process.argv.slice(2);
|
|
30
|
+
if (args.includes('--help') || args.includes('-h') || args.length === 0) {
|
|
31
|
+
printHelp();
|
|
32
|
+
process.exit(0);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
let repoRoot = null;
|
|
36
|
+
let outFile = null;
|
|
37
|
+
|
|
38
|
+
for (let i = 0; i < args.length; i++) {
|
|
39
|
+
const arg = args[i];
|
|
40
|
+
if (arg === '--out' && i + 1 < args.length) {
|
|
41
|
+
outFile = args[++i];
|
|
42
|
+
} else if (!arg.startsWith('-') && !repoRoot) {
|
|
43
|
+
repoRoot = resolve(process.cwd(), arg);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (!repoRoot || !existsSync(repoRoot)) {
|
|
48
|
+
console.error(`Error: Repository path not found: ${repoRoot}`);
|
|
49
|
+
process.exit(1);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Detect framework
|
|
53
|
+
let framework = 'unknown';
|
|
54
|
+
const pkgPath = join(repoRoot, 'package.json');
|
|
55
|
+
if (existsSync(pkgPath)) {
|
|
56
|
+
try {
|
|
57
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
|
|
58
|
+
const deps = { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) };
|
|
59
|
+
if (deps.next) framework = 'nextjs';
|
|
60
|
+
else if (deps['@sveltejs/kit']) framework = 'sveltekit';
|
|
61
|
+
else if (deps.nuxt) framework = 'nuxt';
|
|
62
|
+
else if (deps.astro) framework = 'astro';
|
|
63
|
+
else if (deps.react) framework = 'react';
|
|
64
|
+
} catch {
|
|
65
|
+
// Ignore error
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const routes = [];
|
|
70
|
+
const middleware = [];
|
|
71
|
+
|
|
72
|
+
function walk(dir) {
|
|
73
|
+
let entries;
|
|
74
|
+
try {
|
|
75
|
+
entries = readdirSync(dir, { withFileTypes: true });
|
|
76
|
+
} catch {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
for (const entry of entries) {
|
|
80
|
+
if (entry.name === 'node_modules' || entry.name === '.git' || entry.name === '.next' || entry.name === 'dist') {
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
const fullPath = join(dir, entry.name);
|
|
84
|
+
if (entry.isDirectory()) {
|
|
85
|
+
walk(fullPath);
|
|
86
|
+
} else if (entry.isFile()) {
|
|
87
|
+
const relPath = relative(repoRoot, fullPath).replace(/\\/g, '/');
|
|
88
|
+
const filename = basename(entry.name);
|
|
89
|
+
|
|
90
|
+
// Check middleware
|
|
91
|
+
if (/^middleware\.(ts|js|mjs)$/.test(filename) || /^src\/middleware\.(ts|js|mjs)$/.test(relPath)) {
|
|
92
|
+
try {
|
|
93
|
+
const content = readFileSync(fullPath, 'utf8');
|
|
94
|
+
middleware.push({
|
|
95
|
+
file: relPath,
|
|
96
|
+
hasMatcher: content.includes('matcher:'),
|
|
97
|
+
runtime: content.includes("runtime = 'edge'") ? 'edge' : 'nodejs'
|
|
98
|
+
});
|
|
99
|
+
} catch {}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Check App Router route handlers
|
|
103
|
+
if (/^(route|page)\.(ts|tsx|js|jsx)$/.test(filename) && (relPath.includes('app/') || relPath.includes('src/app/'))) {
|
|
104
|
+
try {
|
|
105
|
+
const content = readFileSync(fullPath, 'utf8');
|
|
106
|
+
const isRoute = filename.startsWith('route');
|
|
107
|
+
const isDynamic = content.includes("dynamic = 'force-dynamic'") || content.includes('export const dynamic = "force-dynamic"');
|
|
108
|
+
const isStatic = content.includes("dynamic = 'force-static'") || content.includes('export const dynamic = "force-static"');
|
|
109
|
+
const hasRevalidate = /revalidate\s*=\s*(\d+)/.test(content);
|
|
110
|
+
const revalidateVal = hasRevalidate ? parseInt(content.match(/revalidate\s*=\s*(\d+)/)[1], 10) : null;
|
|
111
|
+
const runtime = content.includes("runtime = 'edge'") ? 'edge' : 'nodejs';
|
|
112
|
+
|
|
113
|
+
// Extract route pattern
|
|
114
|
+
let routeUrl = relPath
|
|
115
|
+
.replace(/^(src\/)?app/, '')
|
|
116
|
+
.replace(/\/(route|page)\.(ts|tsx|js|jsx)$/, '');
|
|
117
|
+
if (!routeUrl.startsWith('/')) routeUrl = '/' + routeUrl;
|
|
118
|
+
if (routeUrl === '') routeUrl = '/';
|
|
119
|
+
|
|
120
|
+
routes.push({
|
|
121
|
+
type: isRoute ? 'api-route' : 'page-route',
|
|
122
|
+
route: routeUrl,
|
|
123
|
+
file: relPath,
|
|
124
|
+
dynamic: isDynamic ? 'force-dynamic' : (isStatic ? 'force-static' : 'auto'),
|
|
125
|
+
revalidate: revalidateVal,
|
|
126
|
+
runtime
|
|
127
|
+
});
|
|
128
|
+
} catch {}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Check Pages Router API handlers
|
|
132
|
+
if ((relPath.includes('pages/api/') || relPath.includes('src/pages/api/')) && /\.(ts|js)$/.test(filename)) {
|
|
133
|
+
try {
|
|
134
|
+
const content = readFileSync(fullPath, 'utf8');
|
|
135
|
+
let routeUrl = relPath
|
|
136
|
+
.replace(/^(src\/)?pages/, '')
|
|
137
|
+
.replace(/\.(ts|js)$/, '');
|
|
138
|
+
if (routeUrl.endsWith('/index')) routeUrl = routeUrl.slice(0, -6);
|
|
139
|
+
|
|
140
|
+
routes.push({
|
|
141
|
+
type: 'api-route',
|
|
142
|
+
route: routeUrl,
|
|
143
|
+
file: relPath,
|
|
144
|
+
dynamic: 'pages-api',
|
|
145
|
+
runtime: content.includes("runtime = 'edge'") ? 'edge' : 'nodejs'
|
|
146
|
+
});
|
|
147
|
+
} catch {}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
walk(repoRoot);
|
|
154
|
+
|
|
155
|
+
const result = {
|
|
156
|
+
timestamp: new Date().toISOString(),
|
|
157
|
+
repoRoot: relative(process.cwd(), repoRoot).replace(/\\/g, '/') || '.',
|
|
158
|
+
framework,
|
|
159
|
+
summary: {
|
|
160
|
+
totalRoutes: routes.length,
|
|
161
|
+
apiRoutes: routes.filter(r => r.type === 'api-route').length,
|
|
162
|
+
pageRoutes: routes.filter(r => r.type === 'page-route').length,
|
|
163
|
+
middlewareCount: middleware.length
|
|
164
|
+
},
|
|
165
|
+
middleware,
|
|
166
|
+
routes
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
const outputJson = JSON.stringify(result, null, 2);
|
|
170
|
+
if (outFile) {
|
|
171
|
+
writeFileSync(resolve(process.cwd(), outFile), outputJson, 'utf8');
|
|
172
|
+
} else {
|
|
173
|
+
console.log(outputJson);
|
|
174
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
name: vercel-optimize
|
|
2
|
+
type: runtime
|
|
2
3
|
description: Observability-first Vercel performance and cost reduction optimization skill.
|
|
3
4
|
tags:
|
|
4
5
|
- vercel
|
|
@@ -8,3 +9,10 @@ tags:
|
|
|
8
9
|
version: 1.2.0
|
|
9
10
|
requires:
|
|
10
11
|
- nextjs
|
|
12
|
+
resources:
|
|
13
|
+
- SKILL.md
|
|
14
|
+
- VALIDATION.json
|
|
15
|
+
- scripts/collect-signals.mjs
|
|
16
|
+
- scripts/gate-investigations.mjs
|
|
17
|
+
- scripts/merge-signals.mjs
|
|
18
|
+
- scripts/scan-codebase.mjs
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
id: web-accessibility
|
|
2
2
|
name: Web Accessibility
|
|
3
3
|
category: frontend
|
|
4
|
+
type: instruction-only
|
|
4
5
|
tags: [frontend, a11y, wcag, aria, accessibility]
|
|
5
6
|
requires: []
|
|
6
7
|
optional: [react, nextjs]
|
|
@@ -8,3 +9,9 @@ conflicts: []
|
|
|
8
9
|
weight: 7
|
|
9
10
|
documents:
|
|
10
11
|
- accessibility.md
|
|
12
|
+
resources:
|
|
13
|
+
- EXAMPLES.md
|
|
14
|
+
- SKILL.md
|
|
15
|
+
- TROUBLESHOOTING.md
|
|
16
|
+
- VALIDATION.json
|
|
17
|
+
- accessibility.md
|
package/.agents/ctx.js
CHANGED
|
@@ -370,7 +370,20 @@ if (command === 'export') {
|
|
|
370
370
|
// ── doctor ────────────────────────────────────────────────────────────────────
|
|
371
371
|
} else if (command === 'doctor') {
|
|
372
372
|
const doctorModule = require('./doctor.js');
|
|
373
|
-
doctorModule.runDoctor(process.cwd());
|
|
373
|
+
const res = doctorModule.runDoctor(process.cwd(), { json: args.includes('--json') });
|
|
374
|
+
if (res && res.ok === false) {
|
|
375
|
+
process.exit(1);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// ── status ────────────────────────────────────────────────────────────────────
|
|
379
|
+
} else if (command === 'status') {
|
|
380
|
+
const commands = require(path.join(__dirname, '..', 'bin', 'commands.js'));
|
|
381
|
+
const status = commands.getStatus(process.cwd());
|
|
382
|
+
if (args.includes('--json')) {
|
|
383
|
+
console.log(JSON.stringify(status, null, 2));
|
|
384
|
+
} else {
|
|
385
|
+
console.log(commands.formatStatusText(status));
|
|
386
|
+
}
|
|
374
387
|
|
|
375
388
|
// ── stats ─────────────────────────────────────────────────────────────────────
|
|
376
389
|
} else if (command === 'stats') {
|
package/.agents/doctor.js
CHANGED
|
@@ -112,6 +112,100 @@ function checkCompiledAdapters(projectDir) {
|
|
|
112
112
|
return compiled;
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
+
function checkLockfileIntegrity(projectDir) {
|
|
116
|
+
const lockPath = path.join(projectDir, '.agents', 'contextos.lock.json');
|
|
117
|
+
if (!fs.existsSync(lockPath)) {
|
|
118
|
+
return { ok: true, exists: false, isError: false, message: 'none (not generated yet)' };
|
|
119
|
+
}
|
|
120
|
+
try {
|
|
121
|
+
const raw = fs.readFileSync(lockPath, 'utf8');
|
|
122
|
+
const data = JSON.parse(raw);
|
|
123
|
+
if (!data || typeof data !== 'object') {
|
|
124
|
+
return { ok: false, exists: true, isError: true, error: 'Lockfile JSON root is not an object' };
|
|
125
|
+
}
|
|
126
|
+
if (!data.schemaVersion || typeof data.schemaVersion !== 'number') {
|
|
127
|
+
return { ok: false, exists: true, isError: true, error: 'Lockfile missing valid schemaVersion' };
|
|
128
|
+
}
|
|
129
|
+
if (!data.managedFiles || typeof data.managedFiles !== 'object') {
|
|
130
|
+
return { ok: false, exists: true, isError: true, error: 'Lockfile missing managedFiles map' };
|
|
131
|
+
}
|
|
132
|
+
const count = Object.keys(data.managedFiles).length;
|
|
133
|
+
return { ok: true, exists: true, isError: false, message: `valid (v${data.schemaVersion}, ${count} managed files)` };
|
|
134
|
+
} catch (err) {
|
|
135
|
+
return { ok: false, exists: true, isError: true, error: `Lockfile is corrupted JSON: ${err.message}` };
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function checkAdapterIntegrity(projectDir) {
|
|
140
|
+
const errors = [];
|
|
141
|
+
const filesToCheck = [
|
|
142
|
+
path.join(projectDir, '.cursorrules'),
|
|
143
|
+
path.join(projectDir, '.github', 'copilot-instructions.md'),
|
|
144
|
+
path.join(projectDir, '.aider.conf.yml'),
|
|
145
|
+
path.join(projectDir, '.zed', 'rules.md'),
|
|
146
|
+
];
|
|
147
|
+
for (const f of filesToCheck) {
|
|
148
|
+
if (fs.existsSync(f)) {
|
|
149
|
+
try {
|
|
150
|
+
const stat = fs.statSync(f);
|
|
151
|
+
if (stat.size === 0) {
|
|
152
|
+
errors.push(`Adapter file ${path.relative(projectDir, f).replace(/\\/g, '/')} is empty (0 bytes)`);
|
|
153
|
+
}
|
|
154
|
+
} catch {
|
|
155
|
+
errors.push(`Adapter file ${path.relative(projectDir, f).replace(/\\/g, '/')} is unreadable`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const cursorRulesDir = path.join(projectDir, '.cursor', 'rules');
|
|
161
|
+
if (fs.existsSync(cursorRulesDir)) {
|
|
162
|
+
try {
|
|
163
|
+
const files = fs.readdirSync(cursorRulesDir);
|
|
164
|
+
for (const file of files) {
|
|
165
|
+
const full = path.join(cursorRulesDir, file);
|
|
166
|
+
if (fs.statSync(full).size === 0) {
|
|
167
|
+
errors.push(`Cursor rule ${file} is empty (0 bytes)`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
} catch {}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return { ok: errors.length === 0, errors };
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function checkSymlinks(projectDir) {
|
|
177
|
+
const errors = [];
|
|
178
|
+
const targetDir = path.join(projectDir, '.agents');
|
|
179
|
+
if (!fs.existsSync(targetDir)) return { ok: true, errors: [] };
|
|
180
|
+
|
|
181
|
+
function scan(current) {
|
|
182
|
+
let entries;
|
|
183
|
+
try {
|
|
184
|
+
entries = fs.readdirSync(current);
|
|
185
|
+
} catch {
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
for (const name of entries) {
|
|
189
|
+
const full = path.join(current, name);
|
|
190
|
+
try {
|
|
191
|
+
const lstat = fs.lstatSync(full);
|
|
192
|
+
if (lstat.isSymbolicLink()) {
|
|
193
|
+
if (!fs.existsSync(full)) {
|
|
194
|
+
errors.push(`Broken symlink: ${path.relative(projectDir, full).replace(/\\/g, '/')}`);
|
|
195
|
+
}
|
|
196
|
+
} else if (lstat.isDirectory()) {
|
|
197
|
+
scan(full);
|
|
198
|
+
}
|
|
199
|
+
} catch {
|
|
200
|
+
errors.push(`Cannot stat file: ${path.relative(projectDir, full).replace(/\\/g, '/')}`);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
scan(targetDir);
|
|
206
|
+
return { ok: errors.length === 0, errors };
|
|
207
|
+
}
|
|
208
|
+
|
|
115
209
|
function inspectSkills(projectDir, activeProfile) {
|
|
116
210
|
const skillsDir = path.join(projectDir, '.agents', 'core', 'skills');
|
|
117
211
|
if (!fs.existsSync(skillsDir)) return { total: 0, active: 0, excluded: 0, byDomain: { frontend: [], backend: [], cross: [] } };
|
|
@@ -139,18 +233,47 @@ function inspectSkills(projectDir, activeProfile) {
|
|
|
139
233
|
};
|
|
140
234
|
}
|
|
141
235
|
|
|
142
|
-
function runDoctor(projectDir = process.cwd()) {
|
|
236
|
+
function runDoctor(projectDir = process.cwd(), options = {}) {
|
|
143
237
|
const version = (() => {
|
|
144
238
|
try {
|
|
145
239
|
return require('../package.json').version;
|
|
146
240
|
} catch {
|
|
147
|
-
return '1.
|
|
241
|
+
return '1.7.0';
|
|
148
242
|
}
|
|
149
243
|
})();
|
|
150
244
|
|
|
245
|
+
const isJson = Boolean(options.json);
|
|
246
|
+
const errors = [];
|
|
247
|
+
const warnings = [];
|
|
248
|
+
|
|
151
249
|
const agentsDir = path.join(projectDir, '.agents');
|
|
152
250
|
const hasAgents = fs.existsSync(agentsDir);
|
|
251
|
+
if (!hasAgents) {
|
|
252
|
+
errors.push('.agents/ directory missing (run: npx contextos-agents init)');
|
|
253
|
+
}
|
|
254
|
+
|
|
153
255
|
const nodeCheck = checkNodeVersion();
|
|
256
|
+
if (!nodeCheck.ok) {
|
|
257
|
+
errors.push(nodeCheck.message);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const lockfileCheck = checkLockfileIntegrity(projectDir);
|
|
261
|
+
if (!lockfileCheck.ok) {
|
|
262
|
+
errors.push(lockfileCheck.error);
|
|
263
|
+
} else if (!lockfileCheck.exists && hasAgents) {
|
|
264
|
+
warnings.push('Lockfile missing (.agents/contextos.lock.json)');
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const adapterCheck = checkAdapterIntegrity(projectDir);
|
|
268
|
+
if (!adapterCheck.ok) {
|
|
269
|
+
errors.push(...adapterCheck.errors);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const symlinkCheck = checkSymlinks(projectDir);
|
|
273
|
+
if (!symlinkCheck.ok) {
|
|
274
|
+
errors.push(...symlinkCheck.errors);
|
|
275
|
+
}
|
|
276
|
+
|
|
154
277
|
const activeProfile = hasAgents ? profiles.getActiveProfile(projectDir) : null;
|
|
155
278
|
const skillsInfo = inspectSkills(projectDir, activeProfile);
|
|
156
279
|
const compiledAdapters = checkCompiledAdapters(projectDir);
|
|
@@ -162,6 +285,34 @@ function runDoctor(projectDir = process.cwd()) {
|
|
|
162
285
|
const secretCheck = checkSecretScanner(projectDir);
|
|
163
286
|
const stack = profiles.detectStack(projectDir);
|
|
164
287
|
|
|
288
|
+
const ok = errors.length === 0;
|
|
289
|
+
|
|
290
|
+
if (isJson) {
|
|
291
|
+
const report = {
|
|
292
|
+
ok,
|
|
293
|
+
version,
|
|
294
|
+
errors,
|
|
295
|
+
warnings,
|
|
296
|
+
hasAgents,
|
|
297
|
+
nodeOk: nodeCheck.ok,
|
|
298
|
+
activeProfile,
|
|
299
|
+
skillsInfo,
|
|
300
|
+
compiledAdapters,
|
|
301
|
+
hasMcp,
|
|
302
|
+
hookCheck,
|
|
303
|
+
secretCheck,
|
|
304
|
+
lockfileCheck,
|
|
305
|
+
adapterCheck,
|
|
306
|
+
symlinkCheck,
|
|
307
|
+
stack,
|
|
308
|
+
};
|
|
309
|
+
console.log(JSON.stringify(report, null, 2));
|
|
310
|
+
if (options.exitOnError !== false && !ok && require.main === module) {
|
|
311
|
+
process.exit(1);
|
|
312
|
+
}
|
|
313
|
+
return report;
|
|
314
|
+
}
|
|
315
|
+
|
|
165
316
|
console.log('\n┌─────────────────────────────────────────────────────────────┐');
|
|
166
317
|
console.log(`│ ContextOS Doctor — Project Health Check v${version.padEnd(16)}│`);
|
|
167
318
|
console.log('├─────────────────────────────────────────────────────────────┤');
|
|
@@ -178,6 +329,15 @@ function runDoctor(projectDir = process.cwd()) {
|
|
|
178
329
|
const nodeIcon = nodeCheck.ok ? '✓' : '✗';
|
|
179
330
|
console.log(`│ ${nodeIcon} ${nodeCheck.message.padEnd(58)}│`);
|
|
180
331
|
|
|
332
|
+
// Lockfile
|
|
333
|
+
if (lockfileCheck.exists) {
|
|
334
|
+
const lockIcon = lockfileCheck.ok ? '✓' : '✗';
|
|
335
|
+
const lockStr = `Lockfile: ${lockfileCheck.ok ? lockfileCheck.message : lockfileCheck.error}`;
|
|
336
|
+
console.log(`│ ${lockIcon} ${lockStr.slice(0, 58).padEnd(58)}│`);
|
|
337
|
+
} else {
|
|
338
|
+
console.log('│ • Lockfile: not present (optional for dev repository) │');
|
|
339
|
+
}
|
|
340
|
+
|
|
181
341
|
// Active profile
|
|
182
342
|
if (activeProfile) {
|
|
183
343
|
const profStr = `Active profile: ${activeProfile.name || activeProfile.profile}`;
|
|
@@ -199,6 +359,11 @@ function runDoctor(projectDir = process.cwd()) {
|
|
|
199
359
|
console.log('│ • Adapters compiled: none (run: contextos export gemini) │');
|
|
200
360
|
}
|
|
201
361
|
|
|
362
|
+
// Symlinks
|
|
363
|
+
const symIcon = symlinkCheck.ok ? '✓' : '✗';
|
|
364
|
+
const symStr = symlinkCheck.ok ? 'Symlinks: verified (0 broken)' : `Broken symlinks: ${symlinkCheck.errors.length}`;
|
|
365
|
+
console.log(`│ ${symIcon} ${symStr.padEnd(58)}│`);
|
|
366
|
+
|
|
202
367
|
// MCP
|
|
203
368
|
if (hasMcp) {
|
|
204
369
|
console.log('│ ✓ MCP server: installed (.agents/mcp/server.mjs) │');
|
|
@@ -242,7 +407,18 @@ function runDoctor(projectDir = process.cwd()) {
|
|
|
242
407
|
console.log('│ │');
|
|
243
408
|
console.log('└─────────────────────────────────────────────────────────────┘\n');
|
|
244
409
|
|
|
245
|
-
|
|
410
|
+
if (errors.length > 0) {
|
|
411
|
+
console.error(`[ERROR] Diagnostic check failed with ${errors.length} error(s):`);
|
|
412
|
+
for (const err of errors) {
|
|
413
|
+
console.error(` - ${err}`);
|
|
414
|
+
}
|
|
415
|
+
console.error('');
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
const result = {
|
|
419
|
+
ok,
|
|
420
|
+
errors,
|
|
421
|
+
warnings,
|
|
246
422
|
hasAgents,
|
|
247
423
|
nodeOk: nodeCheck.ok,
|
|
248
424
|
activeProfile,
|
|
@@ -251,12 +427,22 @@ function runDoctor(projectDir = process.cwd()) {
|
|
|
251
427
|
hasMcp,
|
|
252
428
|
hookCheck,
|
|
253
429
|
secretCheck,
|
|
430
|
+
lockfileCheck,
|
|
431
|
+
adapterCheck,
|
|
432
|
+
symlinkCheck,
|
|
254
433
|
stack,
|
|
255
434
|
};
|
|
435
|
+
|
|
436
|
+
if (options.exitOnError !== false && !ok && require.main === module) {
|
|
437
|
+
process.exit(1);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
return result;
|
|
256
441
|
}
|
|
257
442
|
|
|
258
443
|
if (require.main === module) {
|
|
259
|
-
runDoctor();
|
|
444
|
+
const res = runDoctor();
|
|
445
|
+
if (!res.ok) process.exit(1);
|
|
260
446
|
}
|
|
261
447
|
|
|
262
448
|
module.exports = {
|
|
@@ -265,5 +451,8 @@ module.exports = {
|
|
|
265
451
|
checkPreCommitHook,
|
|
266
452
|
checkSecretScanner,
|
|
267
453
|
checkCompiledAdapters,
|
|
454
|
+
checkLockfileIntegrity,
|
|
455
|
+
checkAdapterIntegrity,
|
|
456
|
+
checkSymlinks,
|
|
268
457
|
inspectSkills,
|
|
269
458
|
};
|
|
@@ -33,7 +33,7 @@ Inspired by [addyosmani/agent-skills](https://github.com/addyosmani/agent-skills
|
|
|
33
33
|
```
|
|
34
34
|
|
|
35
35
|
**IRON RULE**: In interactive development, no phase can be skipped and no code is written before `/plan` is approved.
|
|
36
|
-
**Direct Build Exception**: When the prompt/caller explicitly requests a standalone implementation,
|
|
36
|
+
**Direct Build & Fast-Track Exception**: When the prompt/caller explicitly requests a standalone implementation, declares `[PHASE: Build]`, or requests routine operational/maintenance tasks (git operations, version bumps, typo fixes, small config tweaks, diagnostic checks), proceed directly to execution without conversational approval pauses.
|
|
37
37
|
|
|
38
38
|
---
|
|
39
39
|
|
|
@@ -82,6 +82,10 @@ Activate whenever:
|
|
|
82
82
|
- State what was inspected or verified from the code.
|
|
83
83
|
- State the architectural decision made and the immediate next action.
|
|
84
84
|
- Keep narration crisp and actionable without excessive verbosity.
|
|
85
|
+
- **Zero-Spam Constraint**:
|
|
86
|
+
- ❌ **Forbidden**: Starting every intermediate step, tool call, or status update with domain/phase/role tags (e.g. `[DOMAIN: ...] [PHASE: ...] [ROLE: ...]`).
|
|
87
|
+
- ✅ **Mandatory**: Declare role and phase strictly once at the start of a phase. Intermediate step updates must be clean, natural language sentences describing technical actions directly.
|
|
88
|
+
- Do not narrate routine micro-inspections (single line reads or basic greps). Announce only meaningful task phases and decisions.
|
|
85
89
|
|
|
86
90
|
---
|
|
87
91
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## Overview
|
|
4
4
|
|
|
5
|
-
**Graphify** is
|
|
5
|
+
**Graphify** is an instruction-only codebase mapping and context optimization guide. Instead of feeding raw directory trees or entire source files into an agent's context window, Graphify instructs agents on how to construct a deterministic, queryable knowledge graph (`graph.json`, `GRAPH_REPORT.md`, `graph.html`) using external companion analyzers (such as the TypeScript AST analyzer in `contextos-mcp` or the external `graphifyy` CLI), keeping the core package 100% zero-dependency without bundled native Tree-sitter binaries.
|
|
6
6
|
|
|
7
7
|
This skill instructs agents how to build, query, and maintain codebase graphs to navigate complex architectures with near-zero token overhead.
|
|
8
8
|
|
|
@@ -18,12 +18,14 @@ Inspired by [Garry Tan's gstack](https://github.com/garrytan/gstack) — shippin
|
|
|
18
18
|
|
|
19
19
|
## Role Identification Protocol
|
|
20
20
|
|
|
21
|
-
At the start of each task, declare your role:
|
|
21
|
+
At the start of each task or major phase switch, declare your role:
|
|
22
22
|
|
|
23
23
|
```
|
|
24
24
|
[ROLE: <Role Name>] — <One-line description of your mandate for this task>
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
+
> **Anti-Spam Invariant**: Declare this role **strictly once per phase**. Never prefix intermediate tool calls, file operations, or step updates with role tags.
|
|
28
|
+
|
|
27
29
|
Then execute ONLY within the constraints of that role.
|
|
28
30
|
|
|
29
31
|
---
|