@nerviq/cli 1.0.1 → 1.2.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/bin/cli.js +170 -73
- package/package.json +1 -1
- package/src/activity.js +20 -0
- package/src/aider/domain-packs.js +27 -2
- package/src/aider/mcp-packs.js +231 -0
- package/src/aider/techniques.js +3210 -1397
- package/src/audit.js +257 -2
- package/src/catalog.js +18 -2
- package/src/codex/domain-packs.js +23 -1
- package/src/codex/mcp-packs.js +254 -0
- package/src/codex/techniques.js +4738 -3257
- package/src/copilot/domain-packs.js +23 -1
- package/src/copilot/mcp-packs.js +254 -0
- package/src/copilot/techniques.js +3433 -1936
- package/src/cursor/domain-packs.js +23 -1
- package/src/cursor/mcp-packs.js +257 -0
- package/src/cursor/techniques.js +3697 -1869
- package/src/deprecation.js +98 -0
- package/src/domain-pack-expansion.js +571 -0
- package/src/domain-packs.js +25 -2
- package/src/formatters/otel.js +151 -0
- package/src/gemini/domain-packs.js +23 -1
- package/src/gemini/mcp-packs.js +257 -0
- package/src/gemini/techniques.js +3734 -2238
- package/src/integrations.js +194 -0
- package/src/mcp-packs.js +233 -0
- package/src/opencode/domain-packs.js +23 -1
- package/src/opencode/mcp-packs.js +231 -0
- package/src/opencode/techniques.js +3500 -1687
- package/src/org.js +68 -0
- package/src/source-urls.js +410 -260
- package/src/stack-checks.js +565 -0
- package/src/supplemental-checks.js +767 -0
- package/src/techniques.js +2929 -1449
- package/src/telemetry.js +160 -0
- package/src/windsurf/domain-packs.js +23 -1
- package/src/windsurf/mcp-packs.js +257 -0
- package/src/windsurf/techniques.js +3647 -1834
- package/src/workspace.js +233 -0
package/src/workspace.js
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
function normalizePath(value) {
|
|
5
|
+
return value.replace(/\\/g, '/').replace(/^\.\//, '').replace(/\/+$/, '');
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function readJsonSafe(filePath) {
|
|
9
|
+
try {
|
|
10
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
11
|
+
} catch {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function readTextSafe(filePath) {
|
|
17
|
+
try {
|
|
18
|
+
return fs.readFileSync(filePath, 'utf8');
|
|
19
|
+
} catch {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function unique(items) {
|
|
25
|
+
return [...new Set(items.filter(Boolean).map(normalizePath))];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function hasWorkspaceConfig(dir) {
|
|
29
|
+
return [
|
|
30
|
+
'turbo.json',
|
|
31
|
+
'lerna.json',
|
|
32
|
+
'pnpm-workspace.yaml',
|
|
33
|
+
].some((file) => fs.existsSync(path.join(dir, file))) ||
|
|
34
|
+
Boolean(readJsonSafe(path.join(dir, 'package.json'))?.workspaces);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function packageWorkspacePatterns(dir) {
|
|
38
|
+
const pkg = readJsonSafe(path.join(dir, 'package.json')) || {};
|
|
39
|
+
const workspaces = pkg.workspaces;
|
|
40
|
+
|
|
41
|
+
if (Array.isArray(workspaces)) {
|
|
42
|
+
return workspaces;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (workspaces && Array.isArray(workspaces.packages)) {
|
|
46
|
+
return workspaces.packages;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return [];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function lernaWorkspacePatterns(dir) {
|
|
53
|
+
const lerna = readJsonSafe(path.join(dir, 'lerna.json')) || {};
|
|
54
|
+
return Array.isArray(lerna.packages) ? lerna.packages : [];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function pnpmWorkspacePatterns(dir) {
|
|
58
|
+
const content = readTextSafe(path.join(dir, 'pnpm-workspace.yaml'));
|
|
59
|
+
if (!content) return [];
|
|
60
|
+
|
|
61
|
+
const matches = [];
|
|
62
|
+
for (const line of content.split(/\r?\n/)) {
|
|
63
|
+
const match = line.match(/^\s*-\s*["']?([^"']+)["']?\s*$/);
|
|
64
|
+
if (match) {
|
|
65
|
+
matches.push(match[1]);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return matches;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function turboWorkspacePatterns(dir) {
|
|
72
|
+
if (!fs.existsSync(path.join(dir, 'turbo.json'))) {
|
|
73
|
+
return [];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const commonPatterns = [];
|
|
77
|
+
for (const candidate of ['apps', 'packages', 'services']) {
|
|
78
|
+
if (fs.existsSync(path.join(dir, candidate)) && fs.statSync(path.join(dir, candidate)).isDirectory()) {
|
|
79
|
+
commonPatterns.push(`${candidate}/*`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return commonPatterns;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function listDirectories(rootDir) {
|
|
87
|
+
const found = [];
|
|
88
|
+
const queue = [''];
|
|
89
|
+
|
|
90
|
+
while (queue.length > 0) {
|
|
91
|
+
const relative = queue.shift();
|
|
92
|
+
const full = path.join(rootDir, relative);
|
|
93
|
+
|
|
94
|
+
let entries = [];
|
|
95
|
+
try {
|
|
96
|
+
entries = fs.readdirSync(full, { withFileTypes: true });
|
|
97
|
+
} catch {
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
for (const entry of entries) {
|
|
102
|
+
if (!entry.isDirectory()) continue;
|
|
103
|
+
if (entry.name === 'node_modules' || entry.name === '.git' || entry.name === '.next' || entry.name === 'dist') continue;
|
|
104
|
+
const child = normalizePath(path.join(relative, entry.name));
|
|
105
|
+
found.push(child);
|
|
106
|
+
queue.push(child);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return found;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function globToRegExp(pattern) {
|
|
114
|
+
const normalized = normalizePath(pattern)
|
|
115
|
+
.replace(/\*\*/g, '__DOUBLE_STAR__')
|
|
116
|
+
.replace(/\*/g, '__SINGLE_STAR__')
|
|
117
|
+
.replace(/[.+^${}()|[\]\\]/g, '\\$&')
|
|
118
|
+
.replace(/__DOUBLE_STAR__/g, '.*')
|
|
119
|
+
.replace(/__SINGLE_STAR__/g, '[^/]+');
|
|
120
|
+
return new RegExp(`^${normalized}$`);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function expandWorkspacePatterns(dir, patterns) {
|
|
124
|
+
const normalizedPatterns = unique(Array.isArray(patterns) ? patterns : []);
|
|
125
|
+
if (normalizedPatterns.length === 0) {
|
|
126
|
+
return [];
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const allDirs = listDirectories(dir);
|
|
130
|
+
const matches = [];
|
|
131
|
+
|
|
132
|
+
for (const pattern of normalizedPatterns) {
|
|
133
|
+
const fullPath = path.join(dir, pattern);
|
|
134
|
+
if (!pattern.includes('*') && fs.existsSync(fullPath) && fs.statSync(fullPath).isDirectory()) {
|
|
135
|
+
matches.push(normalizePath(pattern));
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const matcher = globToRegExp(pattern);
|
|
140
|
+
for (const candidate of allDirs) {
|
|
141
|
+
if (matcher.test(candidate)) {
|
|
142
|
+
matches.push(candidate);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return unique(matches);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function detectWorkspaceGlobs(dir) {
|
|
151
|
+
return unique([
|
|
152
|
+
...packageWorkspacePatterns(dir),
|
|
153
|
+
...lernaWorkspacePatterns(dir),
|
|
154
|
+
...pnpmWorkspacePatterns(dir),
|
|
155
|
+
...turboWorkspacePatterns(dir),
|
|
156
|
+
]);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function detectWorkspaces(dir) {
|
|
160
|
+
return expandWorkspacePatterns(dir, detectWorkspaceGlobs(dir));
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function parseWorkspaceSelection(value) {
|
|
164
|
+
if (!value) return [];
|
|
165
|
+
if (Array.isArray(value)) return unique(value);
|
|
166
|
+
return unique(String(value).split(',').map((item) => item.trim()).filter(Boolean));
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
async function auditWorkspaces(dir, workspaceGlobs, platform = 'claude') {
|
|
170
|
+
const { audit } = require('./audit');
|
|
171
|
+
const rootDir = path.resolve(dir);
|
|
172
|
+
const selectedPatterns = parseWorkspaceSelection(workspaceGlobs);
|
|
173
|
+
const sourcePatterns = selectedPatterns.length > 0 ? selectedPatterns : detectWorkspaceGlobs(rootDir);
|
|
174
|
+
const workspacePaths = selectedPatterns.length > 0
|
|
175
|
+
? expandWorkspacePatterns(rootDir, selectedPatterns)
|
|
176
|
+
: detectWorkspaces(rootDir);
|
|
177
|
+
const results = [];
|
|
178
|
+
|
|
179
|
+
for (const workspacePath of workspacePaths) {
|
|
180
|
+
const absPath = path.join(rootDir, workspacePath);
|
|
181
|
+
try {
|
|
182
|
+
const result = await audit({ dir: absPath, platform, silent: true });
|
|
183
|
+
results.push({
|
|
184
|
+
name: path.basename(workspacePath),
|
|
185
|
+
workspace: workspacePath,
|
|
186
|
+
dir: absPath,
|
|
187
|
+
platform,
|
|
188
|
+
score: result.score,
|
|
189
|
+
passed: result.passed,
|
|
190
|
+
total: result.checkCount,
|
|
191
|
+
topAction: result.topNextActions?.[0]?.name || null,
|
|
192
|
+
result,
|
|
193
|
+
});
|
|
194
|
+
} catch (error) {
|
|
195
|
+
results.push({
|
|
196
|
+
name: path.basename(workspacePath),
|
|
197
|
+
workspace: workspacePath,
|
|
198
|
+
dir: absPath,
|
|
199
|
+
platform,
|
|
200
|
+
score: null,
|
|
201
|
+
passed: 0,
|
|
202
|
+
total: 0,
|
|
203
|
+
topAction: null,
|
|
204
|
+
error: error.message,
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const validScores = results.filter((item) => typeof item.score === 'number').map((item) => item.score);
|
|
210
|
+
const averageScore = validScores.length > 0
|
|
211
|
+
? Math.round(validScores.reduce((sum, value) => sum + value, 0) / validScores.length)
|
|
212
|
+
: 0;
|
|
213
|
+
|
|
214
|
+
return {
|
|
215
|
+
rootDir,
|
|
216
|
+
platform,
|
|
217
|
+
patterns: sourcePatterns,
|
|
218
|
+
workspaces: results,
|
|
219
|
+
detectedWorkspaces: workspacePaths,
|
|
220
|
+
workspaceCount: workspacePaths.length,
|
|
221
|
+
averageScore,
|
|
222
|
+
maxScore: validScores.length > 0 ? Math.max(...validScores) : 0,
|
|
223
|
+
minScore: validScores.length > 0 ? Math.min(...validScores) : 0,
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
module.exports = {
|
|
228
|
+
hasWorkspaceConfig,
|
|
229
|
+
detectWorkspaceGlobs,
|
|
230
|
+
detectWorkspaces,
|
|
231
|
+
parseWorkspaceSelection,
|
|
232
|
+
auditWorkspaces,
|
|
233
|
+
};
|