codymaster 4.6.0 → 5.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/CHANGELOG.md +74 -8
- package/README.md +192 -95
- package/dist/advisory-handoff.js +89 -0
- package/dist/advisory-report.js +105 -0
- package/dist/browse-server.js +251 -0
- package/dist/cli/command-registry.js +34 -0
- package/dist/cli/commands/agent.js +120 -0
- package/dist/cli/commands/bench.js +69 -0
- package/dist/cli/commands/brain.js +108 -0
- package/dist/cli/commands/dashboard.js +93 -0
- package/dist/cli/commands/design-studio.js +111 -0
- package/dist/cli/commands/distro.js +25 -0
- package/dist/cli/commands/engineering.js +596 -0
- package/dist/cli/commands/evolve.js +123 -0
- package/dist/cli/commands/mcp-serve.js +104 -0
- package/dist/cli/commands/project.js +324 -0
- package/dist/cli/commands/skill-chain.js +269 -0
- package/dist/cli/commands/system.js +89 -0
- package/dist/cli/commands/task.js +254 -0
- package/dist/cli/update-check.js +83 -0
- package/dist/cm-config.js +92 -0
- package/dist/cm-suggest.js +77 -0
- package/dist/codybench/judges/automated.js +31 -0
- package/dist/codybench/runners/claude-code.js +32 -0
- package/dist/codybench/suites/memory-retention.js +85 -0
- package/dist/codybench/suites/tdd-regression.js +35 -0
- package/dist/codybench/suites/token-efficiency.js +55 -0
- package/dist/codybench/types.js +2 -0
- package/dist/context-db.js +157 -0
- package/dist/continuity.js +2 -6
- package/dist/distro-validate.js +54 -0
- package/dist/execution-analyzer.js +138 -0
- package/dist/guardian-core.js +74 -0
- package/dist/index.js +36 -2759
- package/dist/indexer/skills-lib.js +533 -0
- package/dist/indexer/skills-map.js +1374 -0
- package/dist/indexer/skills.js +16 -0
- package/dist/learning-promoter.js +246 -0
- package/dist/mcp-context-server.js +289 -1
- package/dist/mcp-skills-tools.js +81 -0
- package/dist/retro-summary.js +70 -0
- package/dist/second-opinion-providers.js +79 -0
- package/dist/skill-chain.js +63 -1
- package/dist/skill-evolver.js +456 -0
- package/dist/skill-execution-cache.js +254 -0
- package/dist/smart-brain-router.js +184 -0
- package/dist/sprint-pipeline.js +228 -0
- package/dist/storage-backend.js +14 -67
- package/dist/token-budget.js +88 -0
- package/dist/utils/cli-utils.js +76 -0
- package/dist/utils/skill-utils.js +32 -0
- package/package.json +17 -7
- package/scripts/build-skills.mjs +51 -0
- package/scripts/gate-0-repo-hygiene.js +75 -0
- package/scripts/postinstall.js +34 -28
- package/scripts/security-scan.js +1 -1
- package/scripts/validate-skills.mjs +42 -0
- package/skills/CLAUDE.md +2 -7
- package/skills/_shared/helpers.md +2 -8
- package/skills/cm-ads-tracker/SKILL.md +3 -6
- package/skills/cm-browse/SKILL.md +34 -0
- package/skills/cm-conductor-worktrees/SKILL.md +28 -0
- package/skills/cm-content-factory/SKILL.md +1 -1
- package/skills/cm-content-factory/landing/docs/content/changelog.md +36 -0
- package/skills/cm-content-factory/landing/docs/content/deployment.md +46 -0
- package/skills/cm-content-factory/landing/docs/content/execution-flow.md +67 -0
- package/skills/cm-content-factory/landing/docs/content/memory-system.md +38 -0
- package/skills/cm-content-factory/landing/docs/content/openspace.md +27 -0
- package/skills/cm-content-factory/landing/docs/content/use-cases.md +26 -0
- package/skills/cm-content-factory/landing/docs/content/v5-intro.md +28 -0
- package/skills/cm-content-factory/landing/docs/index.html +240 -0
- package/skills/cm-content-factory/landing/index.html +100 -100
- package/skills/cm-content-factory/landing/script.js +42 -0
- package/skills/cm-content-factory/landing/translations.js +400 -400
- package/skills/cm-continuity/SKILL.md +32 -33
- package/skills/cm-design-studio/SKILL.md +34 -0
- package/skills/cm-ecosystem-roadmap/SKILL.md +15 -0
- package/skills/cm-engineering-meta/SKILL.md +73 -0
- package/skills/cm-growth-hacking/SKILL.md +1 -12
- package/skills/cm-guardian-runtime/SKILL.md +26 -0
- package/skills/cm-mcp-engineering/SKILL.md +22 -0
- package/skills/cm-notebooklm/SKILL.md +1 -17
- package/skills/cm-post-deploy-canary/SKILL.md +22 -0
- package/skills/cm-project-bootstrap/SKILL.md +11 -0
- package/skills/cm-qa-visual-cli/SKILL.md +22 -0
- package/skills/cm-retro-cli/SKILL.md +23 -0
- package/skills/cm-second-opinion-cli/SKILL.md +23 -0
- package/skills/cm-secret-shield/SKILL.md +2 -2
- package/skills/cm-security-gate/SKILL.md +1 -0
- package/skills/cm-skill-chain/SKILL.md +25 -4
- package/skills/cm-skill-evolution/SKILL.md +83 -0
- package/skills/cm-skill-health/SKILL.md +83 -0
- package/skills/cm-skill-index/SKILL.md +11 -3
- package/skills/cm-skill-search/SKILL.md +49 -0
- package/skills/cm-skill-share/SKILL.md +58 -0
- package/skills/cm-sprint-bus/SKILL.md +33 -0
- package/skills/cm-start/SKILL.md +0 -10
- package/skills/cm-tdd/SKILL.md +59 -72
- package/skills/profiles/README.md +21 -0
- package/skills/profiles/core.txt +23 -0
- package/skills/profiles/design.txt +6 -0
- package/skills/profiles/full.txt +62 -0
- package/skills/profiles/growth.txt +10 -0
- package/skills/profiles/knowledge.txt +7 -0
- package/install.sh +0 -901
- package/scripts/test-gemini.js +0 -13
- package/skills/cm-frappe-agent/SKILL.md +0 -134
- package/skills/cm-frappe-agent/agents/doctype-architect.md +0 -596
- package/skills/cm-frappe-agent/agents/erpnext-customizer.md +0 -643
- package/skills/cm-frappe-agent/agents/frappe-backend.md +0 -814
- package/skills/cm-frappe-agent/agents/frappe-custom-frontend.md +0 -557
- package/skills/cm-frappe-agent/agents/frappe-debugger.md +0 -625
- package/skills/cm-frappe-agent/agents/frappe-fixer.md +0 -275
- package/skills/cm-frappe-agent/agents/frappe-frontend.md +0 -660
- package/skills/cm-frappe-agent/agents/frappe-installer.md +0 -158
- package/skills/cm-frappe-agent/agents/frappe-performance.md +0 -307
- package/skills/cm-frappe-agent/agents/frappe-planner.md +0 -419
- package/skills/cm-frappe-agent/agents/frappe-remote-ops.md +0 -153
- package/skills/cm-frappe-agent/agents/github-workflow.md +0 -286
- package/skills/cm-frappe-agent/commands/frappe-app.md +0 -351
- package/skills/cm-frappe-agent/commands/frappe-backend.md +0 -162
- package/skills/cm-frappe-agent/commands/frappe-bench.md +0 -254
- package/skills/cm-frappe-agent/commands/frappe-debug.md +0 -263
- package/skills/cm-frappe-agent/commands/frappe-doctype-create.md +0 -272
- package/skills/cm-frappe-agent/commands/frappe-doctype-field.md +0 -310
- package/skills/cm-frappe-agent/commands/frappe-erpnext.md +0 -210
- package/skills/cm-frappe-agent/commands/frappe-fix.md +0 -59
- package/skills/cm-frappe-agent/commands/frappe-frontend.md +0 -210
- package/skills/cm-frappe-agent/commands/frappe-fullstack.md +0 -243
- package/skills/cm-frappe-agent/commands/frappe-github.md +0 -57
- package/skills/cm-frappe-agent/commands/frappe-install.md +0 -52
- package/skills/cm-frappe-agent/commands/frappe-plan.md +0 -442
- package/skills/cm-frappe-agent/commands/frappe-remote.md +0 -58
- package/skills/cm-frappe-agent/commands/frappe-test.md +0 -356
- package/skills/cm-frappe-agent/docs/README.md +0 -51
- package/skills/cm-frappe-agent/docs/agents-catalog.md +0 -113
- package/skills/cm-frappe-agent/docs/architecture.md +0 -149
- package/skills/cm-frappe-agent/docs/commands-catalog.md +0 -82
- package/skills/cm-frappe-agent/docs/resources-catalog.md +0 -66
- package/skills/cm-frappe-agent/docs/sitemap-urls.txt +0 -52
- package/skills/cm-frappe-agent/docs/sitemap.md +0 -81
- package/skills/cm-frappe-agent/docs/sop/user-guide.md +0 -178
- package/skills/cm-frappe-agent/docs/sop/vibe-coding-guide.md +0 -122
- package/skills/cm-frappe-agent/resources/7-layer-architecture.md +0 -985
- package/skills/cm-frappe-agent/resources/bench_commands.md +0 -73
- package/skills/cm-frappe-agent/resources/code-patterns-guide.md +0 -948
- package/skills/cm-frappe-agent/resources/common_pitfalls.md +0 -266
- package/skills/cm-frappe-agent/resources/doctype-registry.md +0 -158
- package/skills/cm-frappe-agent/resources/installation-guide.md +0 -289
- package/skills/cm-frappe-agent/resources/rest-api-patterns.md +0 -182
- package/skills/cm-frappe-agent/resources/scaffold_checklist.md +0 -82
- package/skills/cm-frappe-agent/resources/upgrade_patterns.md +0 -113
- package/skills/cm-frappe-agent/resources/web-form-patterns.md +0 -252
- package/skills/cm-frappe-agent/skills/bench-commands/SKILL.md +0 -621
- package/skills/cm-frappe-agent/skills/client-scripts/SKILL.md +0 -642
- package/skills/cm-frappe-agent/skills/doctype-patterns/SKILL.md +0 -576
- package/skills/cm-frappe-agent/skills/frappe-api/SKILL.md +0 -740
- package/skills/cm-frappe-agent/skills/remote-operations/SKILL.md +0 -47
- package/skills/cm-frappe-agent/skills/server-scripts/SKILL.md +0 -608
- package/skills/cm-frappe-agent/skills/web-forms/SKILL.md +0 -46
- package/skills/frappe-app-builder.zip +0 -0
|
@@ -0,0 +1,533 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AGENT_FOLDER_MAP = exports.WEB_FRONTEND_EXTENSIONS = exports.FRONTEND_BONUS_SKILLS = exports.FRONTEND_PACKAGES = exports.COMBO_SKILLS_MAP = exports.SKILLS_MAP = void 0;
|
|
4
|
+
exports.parseSettingsGradleModules = parseSettingsGradleModules;
|
|
5
|
+
exports.hasWebFrontendFiles = hasWebFrontendFiles;
|
|
6
|
+
exports.resolveWorkspaces = resolveWorkspaces;
|
|
7
|
+
exports.readGemfile = readGemfile;
|
|
8
|
+
exports.readPackageJson = readPackageJson;
|
|
9
|
+
exports.readDenoJson = readDenoJson;
|
|
10
|
+
exports.getDenoImportNames = getDenoImportNames;
|
|
11
|
+
exports.getAllPackageNames = getAllPackageNames;
|
|
12
|
+
exports.detectTechnologies = detectTechnologies;
|
|
13
|
+
exports.detectCombos = detectCombos;
|
|
14
|
+
exports.detectAgents = detectAgents;
|
|
15
|
+
exports.parseSkillPath = parseSkillPath;
|
|
16
|
+
exports.getInstalledSkillNames = getInstalledSkillNames;
|
|
17
|
+
exports.collectSkills = collectSkills;
|
|
18
|
+
const node_fs_1 = require("node:fs");
|
|
19
|
+
const node_path_1 = require("node:path");
|
|
20
|
+
const node_os_1 = require("node:os");
|
|
21
|
+
var skills_map_1 = require("./skills-map");
|
|
22
|
+
Object.defineProperty(exports, "SKILLS_MAP", { enumerable: true, get: function () { return skills_map_1.SKILLS_MAP; } });
|
|
23
|
+
Object.defineProperty(exports, "COMBO_SKILLS_MAP", { enumerable: true, get: function () { return skills_map_1.COMBO_SKILLS_MAP; } });
|
|
24
|
+
Object.defineProperty(exports, "FRONTEND_PACKAGES", { enumerable: true, get: function () { return skills_map_1.FRONTEND_PACKAGES; } });
|
|
25
|
+
Object.defineProperty(exports, "FRONTEND_BONUS_SKILLS", { enumerable: true, get: function () { return skills_map_1.FRONTEND_BONUS_SKILLS; } });
|
|
26
|
+
Object.defineProperty(exports, "WEB_FRONTEND_EXTENSIONS", { enumerable: true, get: function () { return skills_map_1.WEB_FRONTEND_EXTENSIONS; } });
|
|
27
|
+
Object.defineProperty(exports, "AGENT_FOLDER_MAP", { enumerable: true, get: function () { return skills_map_1.AGENT_FOLDER_MAP; } });
|
|
28
|
+
const skills_map_2 = require("./skills-map");
|
|
29
|
+
// ── Internal Constants ───────────────────────────────────────
|
|
30
|
+
const AGENT_FOLDER_ENTRIES = Object.entries(skills_map_2.AGENT_FOLDER_MAP);
|
|
31
|
+
const SCAN_SKIP_DIRS = new Set([
|
|
32
|
+
"node_modules",
|
|
33
|
+
".git",
|
|
34
|
+
"vendor",
|
|
35
|
+
".next",
|
|
36
|
+
"dist",
|
|
37
|
+
"build",
|
|
38
|
+
".output",
|
|
39
|
+
".nuxt",
|
|
40
|
+
".svelte-kit",
|
|
41
|
+
"__pycache__",
|
|
42
|
+
".cache",
|
|
43
|
+
"coverage",
|
|
44
|
+
".turbo",
|
|
45
|
+
".terraform",
|
|
46
|
+
"var",
|
|
47
|
+
"bin",
|
|
48
|
+
"obj",
|
|
49
|
+
".vs",
|
|
50
|
+
]);
|
|
51
|
+
const GRADLE_SCAN_ROOT_FILES = [
|
|
52
|
+
"build.gradle.kts",
|
|
53
|
+
"build.gradle",
|
|
54
|
+
"settings.gradle.kts",
|
|
55
|
+
"settings.gradle",
|
|
56
|
+
"gradle/libs.versions.toml",
|
|
57
|
+
];
|
|
58
|
+
const DOTNET_SCAN_ROOT_FILES = [
|
|
59
|
+
"global.json",
|
|
60
|
+
"NuGet.Config",
|
|
61
|
+
"Directory.Build.props",
|
|
62
|
+
"Directory.Packages.props",
|
|
63
|
+
];
|
|
64
|
+
// ── Gradle Scanning ──────────────────────────────────────────
|
|
65
|
+
function parseSettingsGradleModules(content) {
|
|
66
|
+
const modules = [];
|
|
67
|
+
const includeRe = /include\s*\(?\s*([^)]+)/g;
|
|
68
|
+
let includeMatch;
|
|
69
|
+
while ((includeMatch = includeRe.exec(content)) !== null) {
|
|
70
|
+
const args = includeMatch[1];
|
|
71
|
+
const quotedRe = /['"]([^'"]+)['"]/g;
|
|
72
|
+
let quotedMatch;
|
|
73
|
+
while ((quotedMatch = quotedRe.exec(args)) !== null) {
|
|
74
|
+
modules.push(quotedMatch[1].replace(/^:/, "").replace(/:/g, "/"));
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return modules;
|
|
78
|
+
}
|
|
79
|
+
const _gradleCache = new Map();
|
|
80
|
+
function gradleLayoutCandidatePaths(projectDir) {
|
|
81
|
+
const cached = _gradleCache.get(projectDir);
|
|
82
|
+
if (cached)
|
|
83
|
+
return cached;
|
|
84
|
+
const candidates = [];
|
|
85
|
+
const seen = new Set();
|
|
86
|
+
function add(filePath) {
|
|
87
|
+
if (!seen.has(filePath)) {
|
|
88
|
+
candidates.push(filePath);
|
|
89
|
+
seen.add(filePath);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
for (const f of GRADLE_SCAN_ROOT_FILES) {
|
|
93
|
+
add((0, node_path_1.join)(projectDir, f));
|
|
94
|
+
}
|
|
95
|
+
let entries;
|
|
96
|
+
try {
|
|
97
|
+
entries = (0, node_fs_1.readdirSync)(projectDir, { withFileTypes: true });
|
|
98
|
+
}
|
|
99
|
+
catch (_a) {
|
|
100
|
+
entries = [];
|
|
101
|
+
}
|
|
102
|
+
for (const e of entries) {
|
|
103
|
+
if (!e.isDirectory() || e.name.startsWith(".") || SCAN_SKIP_DIRS.has(e.name))
|
|
104
|
+
continue;
|
|
105
|
+
for (const g of ["build.gradle.kts", "build.gradle"]) {
|
|
106
|
+
add((0, node_path_1.join)(projectDir, e.name, g));
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
for (const settingsFile of ["settings.gradle.kts", "settings.gradle"]) {
|
|
110
|
+
const settingsPath = (0, node_path_1.join)(projectDir, settingsFile);
|
|
111
|
+
let content;
|
|
112
|
+
try {
|
|
113
|
+
content = (0, node_fs_1.readFileSync)(settingsPath, "utf-8");
|
|
114
|
+
}
|
|
115
|
+
catch (_b) {
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
for (const modulePath of parseSettingsGradleModules(content)) {
|
|
119
|
+
for (const g of ["build.gradle.kts", "build.gradle"]) {
|
|
120
|
+
add((0, node_path_1.join)(projectDir, modulePath, g));
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
125
|
+
_gradleCache.set(projectDir, candidates);
|
|
126
|
+
return candidates;
|
|
127
|
+
}
|
|
128
|
+
// ── .NET Scanning ────────────────────────────────────────────
|
|
129
|
+
const _dotNetCache = new Map();
|
|
130
|
+
function dotNetLayoutCandidatePaths(projectDir) {
|
|
131
|
+
const cached = _dotNetCache.get(projectDir);
|
|
132
|
+
if (cached)
|
|
133
|
+
return cached;
|
|
134
|
+
const candidates = [];
|
|
135
|
+
const seen = new Set();
|
|
136
|
+
function add(filePath) {
|
|
137
|
+
if (!seen.has(filePath)) {
|
|
138
|
+
candidates.push(filePath);
|
|
139
|
+
seen.add(filePath);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
for (const f of DOTNET_SCAN_ROOT_FILES) {
|
|
143
|
+
add((0, node_path_1.join)(projectDir, f));
|
|
144
|
+
}
|
|
145
|
+
function scan(dir, depth) {
|
|
146
|
+
if (depth > 2)
|
|
147
|
+
return;
|
|
148
|
+
let entries;
|
|
149
|
+
try {
|
|
150
|
+
entries = (0, node_fs_1.readdirSync)(dir, { withFileTypes: true });
|
|
151
|
+
}
|
|
152
|
+
catch (_a) {
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
for (const e of entries) {
|
|
156
|
+
if (e.isFile()) {
|
|
157
|
+
const lower = e.name.toLowerCase();
|
|
158
|
+
if (lower.endsWith(".sln") || lower.endsWith(".csproj") || lower.endsWith(".fsproj")) {
|
|
159
|
+
add((0, node_path_1.join)(dir, e.name));
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
else if (e.isDirectory() && !e.name.startsWith(".") && !SCAN_SKIP_DIRS.has(e.name)) {
|
|
163
|
+
scan((0, node_path_1.join)(dir, e.name), depth + 1);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
scan(projectDir, 0);
|
|
168
|
+
_dotNetCache.set(projectDir, candidates);
|
|
169
|
+
return candidates;
|
|
170
|
+
}
|
|
171
|
+
function resolveConfigFileContentPaths(projectDir, config) {
|
|
172
|
+
if (config.scanGradleLayout) {
|
|
173
|
+
return gradleLayoutCandidatePaths(projectDir);
|
|
174
|
+
}
|
|
175
|
+
if (config.scanDotNetLayout) {
|
|
176
|
+
return dotNetLayoutCandidatePaths(projectDir);
|
|
177
|
+
}
|
|
178
|
+
return (config.files || []).map((f) => (0, node_path_1.join)(projectDir, f));
|
|
179
|
+
}
|
|
180
|
+
// ── Frontend File Scanning ───────────────────────────────────
|
|
181
|
+
function hasWebFrontendFiles(projectDir, maxDepth = 3) {
|
|
182
|
+
function scan(dir, depth) {
|
|
183
|
+
let entries;
|
|
184
|
+
try {
|
|
185
|
+
entries = (0, node_fs_1.readdirSync)(dir, { withFileTypes: true });
|
|
186
|
+
}
|
|
187
|
+
catch (_a) {
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
for (const entry of entries) {
|
|
191
|
+
if (entry.isFile()) {
|
|
192
|
+
const name = entry.name;
|
|
193
|
+
if (name.endsWith(".blade.php"))
|
|
194
|
+
return true;
|
|
195
|
+
const dot = name.lastIndexOf(".");
|
|
196
|
+
if (dot !== -1 && skills_map_2.WEB_FRONTEND_EXTENSIONS.has(name.slice(dot)))
|
|
197
|
+
return true;
|
|
198
|
+
}
|
|
199
|
+
else if (entry.isDirectory() && depth < maxDepth) {
|
|
200
|
+
if (SCAN_SKIP_DIRS.has(entry.name) || entry.name.startsWith("."))
|
|
201
|
+
continue;
|
|
202
|
+
if (scan((0, node_path_1.join)(dir, entry.name), depth + 1))
|
|
203
|
+
return true;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return false;
|
|
207
|
+
}
|
|
208
|
+
return scan(projectDir, 0);
|
|
209
|
+
}
|
|
210
|
+
// ── Workspace Resolution ──────────────────────────────────────
|
|
211
|
+
function parsePnpmWorkspaceYaml(content) {
|
|
212
|
+
const lines = content.split("\n");
|
|
213
|
+
const patterns = [];
|
|
214
|
+
let inPackages = false;
|
|
215
|
+
for (const raw of lines) {
|
|
216
|
+
const line = raw.trim();
|
|
217
|
+
if (line === "packages:" || line === "packages :") {
|
|
218
|
+
inPackages = true;
|
|
219
|
+
continue;
|
|
220
|
+
}
|
|
221
|
+
if (inPackages) {
|
|
222
|
+
if (line.startsWith("- ")) {
|
|
223
|
+
patterns.push(line
|
|
224
|
+
.slice(2)
|
|
225
|
+
.trim()
|
|
226
|
+
.replace(/^['"]|['"]$/g, ""));
|
|
227
|
+
}
|
|
228
|
+
else if (line !== "" && !line.startsWith("#")) {
|
|
229
|
+
break;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
return patterns;
|
|
234
|
+
}
|
|
235
|
+
function expandWorkspacePatterns(projectDir, patterns) {
|
|
236
|
+
const dirs = [];
|
|
237
|
+
for (const pattern of patterns) {
|
|
238
|
+
if (pattern.includes("*")) {
|
|
239
|
+
const parent = (0, node_path_1.join)(projectDir, pattern.replace(/\/?\*.*$/, ""));
|
|
240
|
+
let entries;
|
|
241
|
+
try {
|
|
242
|
+
entries = (0, node_fs_1.readdirSync)(parent, { withFileTypes: true });
|
|
243
|
+
}
|
|
244
|
+
catch (_a) {
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
for (const entry of entries) {
|
|
248
|
+
if (!entry.isDirectory() || SCAN_SKIP_DIRS.has(entry.name) || entry.name.startsWith("."))
|
|
249
|
+
continue;
|
|
250
|
+
const wsDir = (0, node_path_1.join)(parent, entry.name);
|
|
251
|
+
if ((0, node_fs_1.existsSync)((0, node_path_1.join)(wsDir, "package.json")) ||
|
|
252
|
+
(0, node_fs_1.existsSync)((0, node_path_1.join)(wsDir, "deno.json")) ||
|
|
253
|
+
(0, node_fs_1.existsSync)((0, node_path_1.join)(wsDir, "deno.jsonc"))) {
|
|
254
|
+
dirs.push(wsDir);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
else {
|
|
259
|
+
const wsDir = (0, node_path_1.join)(projectDir, pattern);
|
|
260
|
+
if ((0, node_fs_1.existsSync)((0, node_path_1.join)(wsDir, "package.json")) ||
|
|
261
|
+
(0, node_fs_1.existsSync)((0, node_path_1.join)(wsDir, "deno.json")) ||
|
|
262
|
+
(0, node_fs_1.existsSync)((0, node_path_1.join)(wsDir, "deno.jsonc"))) {
|
|
263
|
+
dirs.push(wsDir);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
return dirs;
|
|
268
|
+
}
|
|
269
|
+
function resolveWorkspaces(projectDir, preloaded) {
|
|
270
|
+
const pnpmPath = (0, node_path_1.join)(projectDir, "pnpm-workspace.yaml");
|
|
271
|
+
if ((0, node_fs_1.existsSync)(pnpmPath)) {
|
|
272
|
+
try {
|
|
273
|
+
const content = (0, node_fs_1.readFileSync)(pnpmPath, "utf-8");
|
|
274
|
+
const patterns = parsePnpmWorkspaceYaml(content);
|
|
275
|
+
if (patterns.length > 0) {
|
|
276
|
+
return expandWorkspacePatterns(projectDir, patterns).filter((d) => (0, node_path_1.resolve)(d) !== (0, node_path_1.resolve)(projectDir));
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
catch (_a) { }
|
|
280
|
+
}
|
|
281
|
+
const pkg = (preloaded === null || preloaded === void 0 ? void 0 : preloaded.pkg) !== undefined ? preloaded.pkg : readPackageJson(projectDir);
|
|
282
|
+
if (pkg) {
|
|
283
|
+
const ws = pkg.workspaces;
|
|
284
|
+
const patterns = Array.isArray(ws)
|
|
285
|
+
? ws
|
|
286
|
+
: Array.isArray(ws === null || ws === void 0 ? void 0 : ws.packages)
|
|
287
|
+
? ws.packages
|
|
288
|
+
: null;
|
|
289
|
+
if (patterns && patterns.length > 0) {
|
|
290
|
+
return expandWorkspacePatterns(projectDir, patterns).filter((d) => (0, node_path_1.resolve)(d) !== (0, node_path_1.resolve)(projectDir));
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
const denoJson = (preloaded === null || preloaded === void 0 ? void 0 : preloaded.denoJson) !== undefined ? preloaded.denoJson : readDenoJson(projectDir);
|
|
294
|
+
if (denoJson === null || denoJson === void 0 ? void 0 : denoJson.workspace) {
|
|
295
|
+
const members = Array.isArray(denoJson.workspace) ? denoJson.workspace : [];
|
|
296
|
+
if (members.length > 0) {
|
|
297
|
+
return expandWorkspacePatterns(projectDir, members).filter((d) => (0, node_path_1.resolve)(d) !== (0, node_path_1.resolve)(projectDir));
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
return [];
|
|
301
|
+
}
|
|
302
|
+
// ── Detection ─────────────────────────────────────────────────
|
|
303
|
+
function readGemfile(dir) {
|
|
304
|
+
const gemfilePath = (0, node_path_1.join)(dir, "Gemfile");
|
|
305
|
+
if (!(0, node_fs_1.existsSync)(gemfilePath))
|
|
306
|
+
return [];
|
|
307
|
+
try {
|
|
308
|
+
const content = (0, node_fs_1.readFileSync)(gemfilePath, "utf-8");
|
|
309
|
+
const gems = [];
|
|
310
|
+
const gemRegex = /^\s*gem\s+['"]([^'"]+)['"]/gm;
|
|
311
|
+
let match;
|
|
312
|
+
while ((match = gemRegex.exec(content)) !== null) {
|
|
313
|
+
gems.push(match[1]);
|
|
314
|
+
}
|
|
315
|
+
return gems;
|
|
316
|
+
}
|
|
317
|
+
catch (_a) {
|
|
318
|
+
return [];
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
function readPackageJson(dir) {
|
|
322
|
+
try {
|
|
323
|
+
return JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.join)(dir, "package.json"), "utf-8"));
|
|
324
|
+
}
|
|
325
|
+
catch (_a) {
|
|
326
|
+
return null;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
function readDenoJson(dir) {
|
|
330
|
+
for (const name of ["deno.json", "deno.jsonc"]) {
|
|
331
|
+
try {
|
|
332
|
+
return JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.join)(dir, name), "utf-8"));
|
|
333
|
+
}
|
|
334
|
+
catch (_a) {
|
|
335
|
+
continue;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
return null;
|
|
339
|
+
}
|
|
340
|
+
function getDenoImportNames(denoJson) {
|
|
341
|
+
if (!(denoJson === null || denoJson === void 0 ? void 0 : denoJson.imports))
|
|
342
|
+
return [];
|
|
343
|
+
return Object.values(denoJson.imports)
|
|
344
|
+
.filter((s) => typeof s === "string" && (s.startsWith("npm:") || s.startsWith("jsr:")))
|
|
345
|
+
.map((specifier) => {
|
|
346
|
+
const bare = specifier.replace(/^(?:npm|jsr):/, "");
|
|
347
|
+
if (bare.startsWith("@")) {
|
|
348
|
+
return bare.replace(/^(@[^/]+\/[^@]+).*$/, "$1");
|
|
349
|
+
}
|
|
350
|
+
return bare.replace(/@.*$/, "");
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
function getAllPackageNames(pkg) {
|
|
354
|
+
if (!pkg)
|
|
355
|
+
return [];
|
|
356
|
+
return [
|
|
357
|
+
...Object.keys(pkg.dependencies || {}),
|
|
358
|
+
...Object.keys(pkg.devDependencies || {}),
|
|
359
|
+
];
|
|
360
|
+
}
|
|
361
|
+
function detectTechnologiesInDir(dir, { skipFrontendFiles = false, pkg: preloadedPkg, denoJson: preloadedDeno, } = {}) {
|
|
362
|
+
const pkg = preloadedPkg !== undefined ? preloadedPkg : readPackageJson(dir);
|
|
363
|
+
const allPackages = getAllPackageNames(pkg);
|
|
364
|
+
const deno = preloadedDeno !== undefined ? preloadedDeno : readDenoJson(dir);
|
|
365
|
+
const denoImports = getDenoImportNames(deno);
|
|
366
|
+
const allDepsSet = denoImports.length > 0 ? new Set([...allPackages, ...denoImports]) : new Set(allPackages);
|
|
367
|
+
const allDepsArray = denoImports.length > 0 ? [...allDepsSet] : allPackages;
|
|
368
|
+
let gemNames;
|
|
369
|
+
const detected = [];
|
|
370
|
+
const fileContentCache = new Map();
|
|
371
|
+
const existsCache = new Map();
|
|
372
|
+
function cachedRead(filePath) {
|
|
373
|
+
if (fileContentCache.has(filePath))
|
|
374
|
+
return fileContentCache.get(filePath);
|
|
375
|
+
let content = null;
|
|
376
|
+
try {
|
|
377
|
+
content = (0, node_fs_1.readFileSync)(filePath, "utf-8");
|
|
378
|
+
}
|
|
379
|
+
catch (_a) { }
|
|
380
|
+
fileContentCache.set(filePath, content);
|
|
381
|
+
if (content !== null)
|
|
382
|
+
existsCache.set(filePath, true);
|
|
383
|
+
return content;
|
|
384
|
+
}
|
|
385
|
+
function cachedExists(filePath) {
|
|
386
|
+
if (existsCache.has(filePath))
|
|
387
|
+
return existsCache.get(filePath);
|
|
388
|
+
const result = (0, node_fs_1.existsSync)(filePath);
|
|
389
|
+
existsCache.set(filePath, result);
|
|
390
|
+
return result;
|
|
391
|
+
}
|
|
392
|
+
for (const tech of skills_map_2.SKILLS_MAP) {
|
|
393
|
+
let found = false;
|
|
394
|
+
if (tech.detect.packages) {
|
|
395
|
+
found = tech.detect.packages.some((p) => allDepsSet.has(p));
|
|
396
|
+
}
|
|
397
|
+
if (!found && tech.detect.packagePatterns) {
|
|
398
|
+
found = tech.detect.packagePatterns.some((pattern) => allDepsArray.some((p) => pattern.test(p)));
|
|
399
|
+
}
|
|
400
|
+
if (!found && tech.detect.configFiles) {
|
|
401
|
+
found = tech.detect.configFiles.some((f) => cachedExists((0, node_path_1.join)(dir, f)));
|
|
402
|
+
}
|
|
403
|
+
if (!found && tech.detect.gems) {
|
|
404
|
+
if (gemNames === undefined)
|
|
405
|
+
gemNames = readGemfile(dir);
|
|
406
|
+
found = tech.detect.gems.some((g) => gemNames.includes(g));
|
|
407
|
+
}
|
|
408
|
+
if (!found && tech.detect.configFileContent) {
|
|
409
|
+
const configs = Array.isArray(tech.detect.configFileContent)
|
|
410
|
+
? tech.detect.configFileContent
|
|
411
|
+
: [tech.detect.configFileContent];
|
|
412
|
+
for (const cfg of configs) {
|
|
413
|
+
const paths = resolveConfigFileContentPaths(dir, cfg);
|
|
414
|
+
const { patterns } = cfg;
|
|
415
|
+
for (const filePath of paths) {
|
|
416
|
+
const content = cachedRead(filePath);
|
|
417
|
+
if (content === null)
|
|
418
|
+
continue;
|
|
419
|
+
if (patterns.some((p) => content.includes(p))) {
|
|
420
|
+
found = true;
|
|
421
|
+
break;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
if (found)
|
|
425
|
+
break;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
if (found) {
|
|
429
|
+
detected.push(tech);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
const isFrontendByPackages = allDepsArray.some((p) => skills_map_2.FRONTEND_PACKAGES.has(p));
|
|
433
|
+
const isFrontendByFiles = isFrontendByPackages || skipFrontendFiles ? false : hasWebFrontendFiles(dir);
|
|
434
|
+
return { detected, isFrontendByPackages, isFrontendByFiles };
|
|
435
|
+
}
|
|
436
|
+
function detectTechnologies(projectDir) {
|
|
437
|
+
const pkg = readPackageJson(projectDir);
|
|
438
|
+
const denoJson = readDenoJson(projectDir);
|
|
439
|
+
const root = detectTechnologiesInDir(projectDir, { pkg, denoJson });
|
|
440
|
+
const seenIds = new Map(root.detected.map((t) => [t.id, t]));
|
|
441
|
+
let isFrontend = root.isFrontendByPackages || root.isFrontendByFiles;
|
|
442
|
+
const workspaceDirs = resolveWorkspaces(projectDir, { pkg, denoJson });
|
|
443
|
+
for (const wsDir of workspaceDirs) {
|
|
444
|
+
const ws = detectTechnologiesInDir(wsDir, { skipFrontendFiles: isFrontend });
|
|
445
|
+
for (const tech of ws.detected) {
|
|
446
|
+
if (!seenIds.has(tech.id)) {
|
|
447
|
+
seenIds.set(tech.id, tech);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
if (ws.isFrontendByPackages || ws.isFrontendByFiles) {
|
|
451
|
+
isFrontend = true;
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
const detected = [...seenIds.values()];
|
|
455
|
+
const detectedIds = detected.map((t) => t.id);
|
|
456
|
+
const combos = detectCombos(detectedIds);
|
|
457
|
+
return { detected, isFrontend, combos };
|
|
458
|
+
}
|
|
459
|
+
function detectCombos(detectedIds) {
|
|
460
|
+
const idSet = detectedIds instanceof Set ? detectedIds : new Set(detectedIds);
|
|
461
|
+
return skills_map_2.COMBO_SKILLS_MAP.filter((combo) => combo.requires.every((id) => idSet.has(id)));
|
|
462
|
+
}
|
|
463
|
+
// ── Agent Detection ─────────────────────────────────────────
|
|
464
|
+
function detectAgents(home = (0, node_os_1.homedir)()) {
|
|
465
|
+
const agents = ["universal"];
|
|
466
|
+
for (const [folder, agentName] of AGENT_FOLDER_ENTRIES) {
|
|
467
|
+
if ((0, node_fs_1.existsSync)((0, node_path_1.join)(home, folder, "skills"))) {
|
|
468
|
+
agents.push(agentName);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
return agents;
|
|
472
|
+
}
|
|
473
|
+
function parseSkillPath(skill) {
|
|
474
|
+
if (skill.startsWith("http")) {
|
|
475
|
+
return { repo: skill, skillName: "", full: skill };
|
|
476
|
+
}
|
|
477
|
+
const parts = skill.split("/");
|
|
478
|
+
return {
|
|
479
|
+
repo: parts.slice(0, 2).join("/"),
|
|
480
|
+
skillName: parts.slice(2).join("/"),
|
|
481
|
+
full: skill,
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
// ── Installed Skills Detection ───────────────────────────────
|
|
485
|
+
function getInstalledSkillNames(projectDir) {
|
|
486
|
+
try {
|
|
487
|
+
const lock = JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.join)(projectDir, "skills-lock.json"), "utf-8"));
|
|
488
|
+
if ((lock === null || lock === void 0 ? void 0 : lock.skills) && typeof lock.skills === "object") {
|
|
489
|
+
return new Set(Object.keys(lock.skills));
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
catch (_a) { }
|
|
493
|
+
try {
|
|
494
|
+
const entries = (0, node_fs_1.readdirSync)((0, node_path_1.join)(projectDir, ".agents", "skills"), { withFileTypes: true });
|
|
495
|
+
return new Set(entries.filter((e) => e.isDirectory()).map((e) => e.name));
|
|
496
|
+
}
|
|
497
|
+
catch (_b) { }
|
|
498
|
+
return new Set();
|
|
499
|
+
}
|
|
500
|
+
function collectSkills({ detected, isFrontend, combos = [], installedNames = null, }) {
|
|
501
|
+
const skillMap = new Map();
|
|
502
|
+
const skills = [];
|
|
503
|
+
function addSkill(skill, source) {
|
|
504
|
+
const existing = skillMap.get(skill);
|
|
505
|
+
if (!existing) {
|
|
506
|
+
const installed = installedNames
|
|
507
|
+
? installedNames.has(parseSkillPath(skill).skillName)
|
|
508
|
+
: false;
|
|
509
|
+
const entry = { skill, sources: [source], installed };
|
|
510
|
+
skillMap.set(skill, entry);
|
|
511
|
+
skills.push(entry);
|
|
512
|
+
}
|
|
513
|
+
else if (!existing.sources.includes(source)) {
|
|
514
|
+
existing.sources.push(source);
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
for (const tech of detected) {
|
|
518
|
+
for (const skill of tech.skills) {
|
|
519
|
+
addSkill(skill, tech.name);
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
for (const combo of combos) {
|
|
523
|
+
for (const skill of combo.skills) {
|
|
524
|
+
addSkill(skill, combo.name);
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
if (isFrontend) {
|
|
528
|
+
for (const skill of skills_map_2.FRONTEND_BONUS_SKILLS) {
|
|
529
|
+
addSkill(skill, "Frontend");
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
return skills;
|
|
533
|
+
}
|