@lousy-agents/cli 2.3.4 → 2.5.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/README.md +3 -1
- package/dist/index.js +638 -21
- package/dist/index.js.map +1 -1
- package/dist/mcp-server.js +1040 -10
- package/dist/mcp-server.js.map +1 -1
- package/package.json +2 -2
package/dist/mcp-server.js
CHANGED
|
@@ -42578,6 +42578,27 @@ var yaml_dist = __webpack_require__(1198);
|
|
|
42578
42578
|
return new FileSystemAgentFileGateway();
|
|
42579
42579
|
}
|
|
42580
42580
|
|
|
42581
|
+
;// CONCATENATED MODULE: ./src/entities/copilot-setup.ts
|
|
42582
|
+
/**
|
|
42583
|
+
* Core domain entities for the Copilot Setup Steps feature.
|
|
42584
|
+
* These are the fundamental types that represent the business domain.
|
|
42585
|
+
*/ /**
|
|
42586
|
+
* Types of version files supported for detection
|
|
42587
|
+
*/ /**
|
|
42588
|
+
* Node.js package manager types (in priority order: npm > yarn > pnpm)
|
|
42589
|
+
*/ const NODE_PACKAGE_MANAGERS = [
|
|
42590
|
+
"npm",
|
|
42591
|
+
"yarn",
|
|
42592
|
+
"pnpm"
|
|
42593
|
+
];
|
|
42594
|
+
/**
|
|
42595
|
+
* Python package manager types (in priority order)
|
|
42596
|
+
*/ const PYTHON_PACKAGE_MANAGERS = [
|
|
42597
|
+
"poetry",
|
|
42598
|
+
"pipenv",
|
|
42599
|
+
"pip"
|
|
42600
|
+
];
|
|
42601
|
+
|
|
42581
42602
|
// EXTERNAL MODULE: external "node:fs"
|
|
42582
42603
|
var external_node_fs_ = __webpack_require__(3024);
|
|
42583
42604
|
// EXTERNAL MODULE: ./node_modules/pathe/dist/shared/pathe.M-eThtNZ.mjs
|
|
@@ -44867,12 +44888,102 @@ async function watchConfig(options) {
|
|
|
44867
44888
|
"actions/setup-ruby",
|
|
44868
44889
|
"jdx/mise-action"
|
|
44869
44890
|
];
|
|
44891
|
+
/**
|
|
44892
|
+
* Default package manager mappings
|
|
44893
|
+
* Based on Dependabot supported ecosystems
|
|
44894
|
+
*/ const DEFAULT_PACKAGE_MANAGERS = [
|
|
44895
|
+
// Node.js package managers
|
|
44896
|
+
{
|
|
44897
|
+
type: "npm",
|
|
44898
|
+
manifestFile: "package.json",
|
|
44899
|
+
lockfile: "package-lock.json",
|
|
44900
|
+
installCommand: "npm ci"
|
|
44901
|
+
},
|
|
44902
|
+
{
|
|
44903
|
+
type: "yarn",
|
|
44904
|
+
manifestFile: "package.json",
|
|
44905
|
+
lockfile: "yarn.lock",
|
|
44906
|
+
installCommand: "yarn install --frozen-lockfile"
|
|
44907
|
+
},
|
|
44908
|
+
{
|
|
44909
|
+
type: "pnpm",
|
|
44910
|
+
manifestFile: "package.json",
|
|
44911
|
+
lockfile: "pnpm-lock.yaml",
|
|
44912
|
+
installCommand: "pnpm install --frozen-lockfile"
|
|
44913
|
+
},
|
|
44914
|
+
// Python package managers
|
|
44915
|
+
{
|
|
44916
|
+
type: "pip",
|
|
44917
|
+
manifestFile: "requirements.txt",
|
|
44918
|
+
installCommand: "pip install -r requirements.txt"
|
|
44919
|
+
},
|
|
44920
|
+
{
|
|
44921
|
+
type: "pipenv",
|
|
44922
|
+
manifestFile: "Pipfile",
|
|
44923
|
+
lockfile: "Pipfile.lock",
|
|
44924
|
+
installCommand: "pipenv install --deploy"
|
|
44925
|
+
},
|
|
44926
|
+
{
|
|
44927
|
+
type: "poetry",
|
|
44928
|
+
manifestFile: "pyproject.toml",
|
|
44929
|
+
lockfile: "poetry.lock",
|
|
44930
|
+
requiresLockfile: true,
|
|
44931
|
+
installCommand: "poetry install --no-root"
|
|
44932
|
+
},
|
|
44933
|
+
// Ruby
|
|
44934
|
+
{
|
|
44935
|
+
type: "bundler",
|
|
44936
|
+
manifestFile: "Gemfile",
|
|
44937
|
+
lockfile: "Gemfile.lock",
|
|
44938
|
+
installCommand: "bundle install"
|
|
44939
|
+
},
|
|
44940
|
+
// Rust
|
|
44941
|
+
{
|
|
44942
|
+
type: "cargo",
|
|
44943
|
+
manifestFile: "Cargo.toml",
|
|
44944
|
+
lockfile: "Cargo.lock",
|
|
44945
|
+
installCommand: "cargo build"
|
|
44946
|
+
},
|
|
44947
|
+
// PHP
|
|
44948
|
+
{
|
|
44949
|
+
type: "composer",
|
|
44950
|
+
manifestFile: "composer.json",
|
|
44951
|
+
lockfile: "composer.lock",
|
|
44952
|
+
installCommand: "composer install"
|
|
44953
|
+
},
|
|
44954
|
+
// Java
|
|
44955
|
+
{
|
|
44956
|
+
type: "maven",
|
|
44957
|
+
manifestFile: "pom.xml",
|
|
44958
|
+
installCommand: "mvn install -DskipTests"
|
|
44959
|
+
},
|
|
44960
|
+
{
|
|
44961
|
+
type: "gradle",
|
|
44962
|
+
manifestFile: "build.gradle",
|
|
44963
|
+
installCommand: "gradle build -x test"
|
|
44964
|
+
},
|
|
44965
|
+
// Go
|
|
44966
|
+
{
|
|
44967
|
+
type: "gomod",
|
|
44968
|
+
manifestFile: "go.mod",
|
|
44969
|
+
lockfile: "go.sum",
|
|
44970
|
+
installCommand: "go mod download"
|
|
44971
|
+
},
|
|
44972
|
+
// Dart/Flutter
|
|
44973
|
+
{
|
|
44974
|
+
type: "pub",
|
|
44975
|
+
manifestFile: "pubspec.yaml",
|
|
44976
|
+
lockfile: "pubspec.lock",
|
|
44977
|
+
installCommand: "dart pub get"
|
|
44978
|
+
}
|
|
44979
|
+
];
|
|
44870
44980
|
/**
|
|
44871
44981
|
* Default copilot-setup configuration
|
|
44872
44982
|
*/ const DEFAULT_CONFIG = {
|
|
44873
44983
|
versionFiles: DEFAULT_VERSION_FILES,
|
|
44874
44984
|
setupActions: DEFAULT_SETUP_ACTIONS,
|
|
44875
|
-
setupActionPatterns: DEFAULT_SETUP_ACTION_PATTERNS
|
|
44985
|
+
setupActionPatterns: DEFAULT_SETUP_ACTION_PATTERNS,
|
|
44986
|
+
packageManagers: DEFAULT_PACKAGE_MANAGERS
|
|
44876
44987
|
};
|
|
44877
44988
|
/**
|
|
44878
44989
|
* Loads the copilot-setup configuration using c12
|
|
@@ -44931,6 +45042,7 @@ async function watchConfig(options) {
|
|
|
44931
45042
|
|
|
44932
45043
|
|
|
44933
45044
|
|
|
45045
|
+
|
|
44934
45046
|
/**
|
|
44935
45047
|
* Reads the content of a version file and trims whitespace
|
|
44936
45048
|
*/ async function readVersionFileContent(filePath) {
|
|
@@ -44948,9 +45060,21 @@ async function watchConfig(options) {
|
|
|
44948
45060
|
return this.config;
|
|
44949
45061
|
}
|
|
44950
45062
|
async detectEnvironment(targetDir) {
|
|
44951
|
-
const miseTomlPath = (0,external_node_path_.join)(targetDir, "mise.toml");
|
|
44952
|
-
const hasMise = await file_system_utils_fileExists(miseTomlPath);
|
|
44953
45063
|
const config = await this.getConfig();
|
|
45064
|
+
const hasMise = await this.detectMise(targetDir);
|
|
45065
|
+
const versionFiles = await this.detectVersionFiles(targetDir, config);
|
|
45066
|
+
const packageManagers = await this.detectPackageManagers(targetDir, config);
|
|
45067
|
+
return {
|
|
45068
|
+
hasMise,
|
|
45069
|
+
versionFiles,
|
|
45070
|
+
packageManagers
|
|
45071
|
+
};
|
|
45072
|
+
}
|
|
45073
|
+
async detectMise(targetDir) {
|
|
45074
|
+
const miseTomlPath = (0,external_node_path_.join)(targetDir, "mise.toml");
|
|
45075
|
+
return file_system_utils_fileExists(miseTomlPath);
|
|
45076
|
+
}
|
|
45077
|
+
async detectVersionFiles(targetDir, config) {
|
|
44954
45078
|
const filenameToType = getVersionFilenameToTypeMap(config);
|
|
44955
45079
|
const versionFiles = [];
|
|
44956
45080
|
for (const fileConfig of config.versionFiles){
|
|
@@ -44964,10 +45088,109 @@ async function watchConfig(options) {
|
|
|
44964
45088
|
});
|
|
44965
45089
|
}
|
|
44966
45090
|
}
|
|
44967
|
-
return
|
|
44968
|
-
|
|
44969
|
-
|
|
44970
|
-
|
|
45091
|
+
return versionFiles;
|
|
45092
|
+
}
|
|
45093
|
+
async detectPackageManagers(targetDir, config) {
|
|
45094
|
+
const packageManagers = [];
|
|
45095
|
+
// Helper to check if a package manager type is in a list
|
|
45096
|
+
const isPackageManagerType = (pm, types)=>types.includes(pm.type);
|
|
45097
|
+
const nodePackageManagers = config.packageManagers.filter((pm)=>isPackageManagerType(pm, NODE_PACKAGE_MANAGERS));
|
|
45098
|
+
const pythonPackageManagers = config.packageManagers.filter((pm)=>isPackageManagerType(pm, PYTHON_PACKAGE_MANAGERS));
|
|
45099
|
+
const otherPackageManagers = config.packageManagers.filter((pm)=>!isPackageManagerType(pm, NODE_PACKAGE_MANAGERS) && !isPackageManagerType(pm, PYTHON_PACKAGE_MANAGERS));
|
|
45100
|
+
// Detect Node.js package manager (with prioritization)
|
|
45101
|
+
const nodePackageManager = await this.detectNodePackageManager(targetDir, nodePackageManagers);
|
|
45102
|
+
if (nodePackageManager) {
|
|
45103
|
+
packageManagers.push(nodePackageManager);
|
|
45104
|
+
}
|
|
45105
|
+
// Detect Python package manager (with prioritization)
|
|
45106
|
+
const pythonPackageManager = await this.detectPythonPackageManager(targetDir, pythonPackageManagers);
|
|
45107
|
+
if (pythonPackageManager) {
|
|
45108
|
+
packageManagers.push(pythonPackageManager);
|
|
45109
|
+
}
|
|
45110
|
+
// Detect other package managers
|
|
45111
|
+
const otherDetectedManagers = await this.detectOtherPackageManagers(targetDir, otherPackageManagers);
|
|
45112
|
+
packageManagers.push(...otherDetectedManagers);
|
|
45113
|
+
return packageManagers;
|
|
45114
|
+
}
|
|
45115
|
+
async detectNodePackageManager(targetDir, nodePackageManagers) {
|
|
45116
|
+
const packageJsonPath = (0,external_node_path_.join)(targetDir, "package.json");
|
|
45117
|
+
if (!await file_system_utils_fileExists(packageJsonPath)) {
|
|
45118
|
+
return null;
|
|
45119
|
+
}
|
|
45120
|
+
// Priority order for Node.js package managers: npm > yarn > pnpm
|
|
45121
|
+
const lockfileOrder = [
|
|
45122
|
+
"npm",
|
|
45123
|
+
"yarn",
|
|
45124
|
+
"pnpm"
|
|
45125
|
+
];
|
|
45126
|
+
for (const pmType of lockfileOrder){
|
|
45127
|
+
const pmConfig = nodePackageManagers.find((pm)=>pm.type === pmType);
|
|
45128
|
+
if (!pmConfig?.lockfile) {
|
|
45129
|
+
continue;
|
|
45130
|
+
}
|
|
45131
|
+
const lockfilePath = (0,external_node_path_.join)(targetDir, pmConfig.lockfile);
|
|
45132
|
+
if (await file_system_utils_fileExists(lockfilePath)) {
|
|
45133
|
+
return {
|
|
45134
|
+
type: pmConfig.type,
|
|
45135
|
+
filename: pmConfig.manifestFile,
|
|
45136
|
+
lockfile: pmConfig.lockfile
|
|
45137
|
+
};
|
|
45138
|
+
}
|
|
45139
|
+
}
|
|
45140
|
+
// Default to npm if no lockfile found
|
|
45141
|
+
const npmConfig = nodePackageManagers.find((pm)=>pm.type === "npm");
|
|
45142
|
+
if (npmConfig) {
|
|
45143
|
+
return {
|
|
45144
|
+
type: npmConfig.type,
|
|
45145
|
+
filename: npmConfig.manifestFile,
|
|
45146
|
+
lockfile: undefined
|
|
45147
|
+
};
|
|
45148
|
+
}
|
|
45149
|
+
return null;
|
|
45150
|
+
}
|
|
45151
|
+
async detectPythonPackageManager(targetDir, pythonPackageManagers) {
|
|
45152
|
+
// Priority order for Python package managers: poetry > pipenv > pip
|
|
45153
|
+
for (const pmType of PYTHON_PACKAGE_MANAGERS){
|
|
45154
|
+
const pmConfig = pythonPackageManagers.find((pm)=>pm.type === pmType);
|
|
45155
|
+
if (!pmConfig) {
|
|
45156
|
+
continue;
|
|
45157
|
+
}
|
|
45158
|
+
const manifestPath = (0,external_node_path_.join)(targetDir, pmConfig.manifestFile);
|
|
45159
|
+
if (await file_system_utils_fileExists(manifestPath)) {
|
|
45160
|
+
const lockfilePath = pmConfig.lockfile ? (0,external_node_path_.join)(targetDir, pmConfig.lockfile) : undefined;
|
|
45161
|
+
const hasLockfile = lockfilePath ? await file_system_utils_fileExists(lockfilePath) : false;
|
|
45162
|
+
// Skip if lockfile is required but not present
|
|
45163
|
+
if (pmConfig.requiresLockfile && !hasLockfile) {
|
|
45164
|
+
continue;
|
|
45165
|
+
}
|
|
45166
|
+
return {
|
|
45167
|
+
type: pmConfig.type,
|
|
45168
|
+
filename: pmConfig.manifestFile,
|
|
45169
|
+
lockfile: hasLockfile ? pmConfig.lockfile : undefined
|
|
45170
|
+
};
|
|
45171
|
+
}
|
|
45172
|
+
}
|
|
45173
|
+
return null;
|
|
45174
|
+
}
|
|
45175
|
+
async detectOtherPackageManagers(targetDir, otherPackageManagers) {
|
|
45176
|
+
const packageManagers = [];
|
|
45177
|
+
for (const pmConfig of otherPackageManagers){
|
|
45178
|
+
const manifestPath = (0,external_node_path_.join)(targetDir, pmConfig.manifestFile);
|
|
45179
|
+
if (await file_system_utils_fileExists(manifestPath)) {
|
|
45180
|
+
const lockfilePath = pmConfig.lockfile ? (0,external_node_path_.join)(targetDir, pmConfig.lockfile) : undefined;
|
|
45181
|
+
const hasLockfile = lockfilePath ? await file_system_utils_fileExists(lockfilePath) : false;
|
|
45182
|
+
// Skip if lockfile is required but not present
|
|
45183
|
+
if (pmConfig.requiresLockfile && !hasLockfile) {
|
|
45184
|
+
continue;
|
|
45185
|
+
}
|
|
45186
|
+
packageManagers.push({
|
|
45187
|
+
type: pmConfig.type,
|
|
45188
|
+
filename: pmConfig.manifestFile,
|
|
45189
|
+
lockfile: hasLockfile ? pmConfig.lockfile : undefined
|
|
45190
|
+
});
|
|
45191
|
+
}
|
|
45192
|
+
}
|
|
45193
|
+
return packageManagers;
|
|
44971
45194
|
}
|
|
44972
45195
|
}
|
|
44973
45196
|
/**
|
|
@@ -44976,6 +45199,283 @@ async function watchConfig(options) {
|
|
|
44976
45199
|
return new FileSystemEnvironmentGateway();
|
|
44977
45200
|
}
|
|
44978
45201
|
|
|
45202
|
+
;// CONCATENATED MODULE: ./src/gateways/instruction-analysis-gateway.ts
|
|
45203
|
+
/**
|
|
45204
|
+
* Gateway for analyzing repository instructions for feedback loop coverage
|
|
45205
|
+
*/
|
|
45206
|
+
|
|
45207
|
+
|
|
45208
|
+
/**
|
|
45209
|
+
* File system implementation of instruction analysis gateway
|
|
45210
|
+
*/ class FileSystemInstructionAnalysisGateway {
|
|
45211
|
+
async analyzeCoverage(targetDir, scripts, tools) {
|
|
45212
|
+
// Find all instruction files
|
|
45213
|
+
const instructionFiles = await this.findInstructionFiles(targetDir);
|
|
45214
|
+
// Read and search instruction content
|
|
45215
|
+
const references = [];
|
|
45216
|
+
const documentedTargets = new Set();
|
|
45217
|
+
for (const file of instructionFiles){
|
|
45218
|
+
const content = await (0,promises_.readFile)(file, "utf-8");
|
|
45219
|
+
const lines = content.split("\n");
|
|
45220
|
+
// Check for script references (e.g., "npm test", "npm run build")
|
|
45221
|
+
for (const script of scripts){
|
|
45222
|
+
const scriptRefs = this.findReferencesInContent(script.name, content, lines, file, targetDir);
|
|
45223
|
+
if (scriptRefs.length > 0) {
|
|
45224
|
+
references.push(...scriptRefs);
|
|
45225
|
+
documentedTargets.add(script.name);
|
|
45226
|
+
}
|
|
45227
|
+
}
|
|
45228
|
+
// Check for tool references (e.g., "mise run test", "biome check")
|
|
45229
|
+
for (const tool of tools){
|
|
45230
|
+
const toolRefs = this.findReferencesInContent(tool.name, content, lines, file, targetDir);
|
|
45231
|
+
if (toolRefs.length > 0) {
|
|
45232
|
+
references.push(...toolRefs);
|
|
45233
|
+
documentedTargets.add(tool.name);
|
|
45234
|
+
}
|
|
45235
|
+
}
|
|
45236
|
+
}
|
|
45237
|
+
// Filter mandatory scripts/tools
|
|
45238
|
+
const mandatoryScripts = scripts.filter((s)=>s.isMandatory);
|
|
45239
|
+
const mandatoryTools = tools.filter((t)=>t.isMandatory);
|
|
45240
|
+
const allMandatory = [
|
|
45241
|
+
...mandatoryScripts,
|
|
45242
|
+
...mandatoryTools
|
|
45243
|
+
];
|
|
45244
|
+
// Categorize as missing or documented
|
|
45245
|
+
const missingInInstructions = allMandatory.filter((item)=>!documentedTargets.has(item.name));
|
|
45246
|
+
const documentedInInstructions = allMandatory.filter((item)=>documentedTargets.has(item.name));
|
|
45247
|
+
const totalMandatory = allMandatory.length;
|
|
45248
|
+
const totalDocumented = documentedInInstructions.length;
|
|
45249
|
+
const coveragePercentage = totalMandatory === 0 ? 100 : totalDocumented / totalMandatory * 100;
|
|
45250
|
+
return {
|
|
45251
|
+
missingInInstructions,
|
|
45252
|
+
documentedInInstructions,
|
|
45253
|
+
references,
|
|
45254
|
+
summary: {
|
|
45255
|
+
totalMandatory,
|
|
45256
|
+
totalDocumented,
|
|
45257
|
+
coveragePercentage: Math.round(coveragePercentage * 100) / 100
|
|
45258
|
+
}
|
|
45259
|
+
};
|
|
45260
|
+
}
|
|
45261
|
+
async findInstructionFiles(targetDir) {
|
|
45262
|
+
const files = [];
|
|
45263
|
+
// Check for .github/copilot-instructions.md
|
|
45264
|
+
const copilotInstructions = (0,external_node_path_.join)(targetDir, ".github", "copilot-instructions.md");
|
|
45265
|
+
if (await file_system_utils_fileExists(copilotInstructions)) {
|
|
45266
|
+
files.push(copilotInstructions);
|
|
45267
|
+
}
|
|
45268
|
+
// Check for .github/instructions/*.md
|
|
45269
|
+
const instructionsDir = (0,external_node_path_.join)(targetDir, ".github", "instructions");
|
|
45270
|
+
if (await file_system_utils_fileExists(instructionsDir)) {
|
|
45271
|
+
try {
|
|
45272
|
+
const instructionFiles = await (0,promises_.readdir)(instructionsDir);
|
|
45273
|
+
for (const file of instructionFiles){
|
|
45274
|
+
if (file.endsWith(".md")) {
|
|
45275
|
+
files.push((0,external_node_path_.join)(instructionsDir, file));
|
|
45276
|
+
}
|
|
45277
|
+
}
|
|
45278
|
+
} catch {
|
|
45279
|
+
// Skip directory if we can't read it (e.g., permissions issues)
|
|
45280
|
+
// Similar to how workflow parsing errors are handled
|
|
45281
|
+
}
|
|
45282
|
+
}
|
|
45283
|
+
return files;
|
|
45284
|
+
}
|
|
45285
|
+
findReferencesInContent(target, content, lines, file, targetDir) {
|
|
45286
|
+
const references = [];
|
|
45287
|
+
// Build a case-insensitive, word-boundary-aware pattern for the target.
|
|
45288
|
+
// This reduces false positives from simple substring matches like
|
|
45289
|
+
// "test" in "testing" or "latest", while still matching common
|
|
45290
|
+
// separators such as spaces, punctuation, etc.
|
|
45291
|
+
const escapedTarget = target.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
45292
|
+
const targetPattern = new RegExp(`(?:^|[^\\w])(${escapedTarget})(?=$|[^\\w])`, "i");
|
|
45293
|
+
// Fast path: skip line-by-line processing if the target pattern
|
|
45294
|
+
// never appears in the full content.
|
|
45295
|
+
if (!targetPattern.test(content)) {
|
|
45296
|
+
return references;
|
|
45297
|
+
}
|
|
45298
|
+
// Find line numbers where target appears
|
|
45299
|
+
for(let i = 0; i < lines.length; i++){
|
|
45300
|
+
const line = lines[i];
|
|
45301
|
+
if (targetPattern.test(line)) {
|
|
45302
|
+
// Get context (line before and after if available)
|
|
45303
|
+
const contextLines = [];
|
|
45304
|
+
if (i > 0) contextLines.push(lines[i - 1]);
|
|
45305
|
+
contextLines.push(line);
|
|
45306
|
+
if (i < lines.length - 1) contextLines.push(lines[i + 1]);
|
|
45307
|
+
const relativePath = (0,external_node_path_.relative)(targetDir, file);
|
|
45308
|
+
references.push({
|
|
45309
|
+
target,
|
|
45310
|
+
file: relativePath,
|
|
45311
|
+
line: i + 1,
|
|
45312
|
+
context: contextLines.join("\n")
|
|
45313
|
+
});
|
|
45314
|
+
}
|
|
45315
|
+
}
|
|
45316
|
+
return references;
|
|
45317
|
+
}
|
|
45318
|
+
}
|
|
45319
|
+
/**
|
|
45320
|
+
* Creates and returns the default instruction analysis gateway
|
|
45321
|
+
*/ function createInstructionAnalysisGateway() {
|
|
45322
|
+
return new FileSystemInstructionAnalysisGateway();
|
|
45323
|
+
}
|
|
45324
|
+
|
|
45325
|
+
;// CONCATENATED MODULE: ./src/entities/feedback-loop.ts
|
|
45326
|
+
/**
|
|
45327
|
+
* Core domain entities for SDLC feedback loop discovery and validation.
|
|
45328
|
+
* These types represent scripts, tools, and their mapping to SDLC phases.
|
|
45329
|
+
*/ /**
|
|
45330
|
+
* SDLC feedback loop phases that scripts and tools support
|
|
45331
|
+
*/ /**
|
|
45332
|
+
* Maps common script names to SDLC phases
|
|
45333
|
+
*/ const SCRIPT_PHASE_MAPPING = {
|
|
45334
|
+
test: "test",
|
|
45335
|
+
"test:unit": "test",
|
|
45336
|
+
"test:integration": "test",
|
|
45337
|
+
"test:e2e": "test",
|
|
45338
|
+
"test:watch": "test",
|
|
45339
|
+
build: "build",
|
|
45340
|
+
compile: "build",
|
|
45341
|
+
bundle: "build",
|
|
45342
|
+
lint: "lint",
|
|
45343
|
+
"lint:fix": "lint",
|
|
45344
|
+
"lint:check": "lint",
|
|
45345
|
+
"lint:workflows": "lint",
|
|
45346
|
+
"lint:yaml": "lint",
|
|
45347
|
+
format: "format",
|
|
45348
|
+
"format:check": "format",
|
|
45349
|
+
"format:fix": "format",
|
|
45350
|
+
prettier: "format",
|
|
45351
|
+
"prettier:check": "format",
|
|
45352
|
+
"prettier:fix": "format",
|
|
45353
|
+
audit: "security",
|
|
45354
|
+
"audit:fix": "security",
|
|
45355
|
+
security: "security",
|
|
45356
|
+
deploy: "deploy",
|
|
45357
|
+
publish: "deploy",
|
|
45358
|
+
release: "deploy",
|
|
45359
|
+
install: "install",
|
|
45360
|
+
ci: "install",
|
|
45361
|
+
dev: "dev",
|
|
45362
|
+
start: "dev",
|
|
45363
|
+
serve: "dev"
|
|
45364
|
+
};
|
|
45365
|
+
/**
|
|
45366
|
+
* Scripts that are considered mandatory for agent feedback loops
|
|
45367
|
+
*/ const MANDATORY_SCRIPT_NAMES = (/* unused pure expression or super */ null && ([
|
|
45368
|
+
"test",
|
|
45369
|
+
"build",
|
|
45370
|
+
"lint",
|
|
45371
|
+
"format"
|
|
45372
|
+
]));
|
|
45373
|
+
/**
|
|
45374
|
+
* Determines the SDLC phase for a script based on its name and command
|
|
45375
|
+
*/ function determineScriptPhase(scriptName, command) {
|
|
45376
|
+
// Check exact name match first
|
|
45377
|
+
const exactMatch = SCRIPT_PHASE_MAPPING[scriptName];
|
|
45378
|
+
if (exactMatch) {
|
|
45379
|
+
return exactMatch;
|
|
45380
|
+
}
|
|
45381
|
+
// Check if name starts with a known phase
|
|
45382
|
+
for (const [pattern, phase] of Object.entries(SCRIPT_PHASE_MAPPING)){
|
|
45383
|
+
// Skip patterns we've already handled as exact matches
|
|
45384
|
+
if (pattern === scriptName) {
|
|
45385
|
+
continue;
|
|
45386
|
+
}
|
|
45387
|
+
if (!scriptName.startsWith(pattern)) {
|
|
45388
|
+
continue;
|
|
45389
|
+
}
|
|
45390
|
+
// If the pattern itself includes a separator (e.g. "test:unit"),
|
|
45391
|
+
// allow simple prefix matching (handles "test:unit:watch", etc.)
|
|
45392
|
+
const lastCharOfPattern = pattern.charAt(pattern.length - 1);
|
|
45393
|
+
if (lastCharOfPattern === ":") {
|
|
45394
|
+
return phase;
|
|
45395
|
+
}
|
|
45396
|
+
// For generic patterns like "test" or "build", require that the next
|
|
45397
|
+
// character after the pattern is a colon to avoid matching names
|
|
45398
|
+
// like "test-utils" or "build-tools".
|
|
45399
|
+
const nextChar = scriptName.charAt(pattern.length);
|
|
45400
|
+
if (nextChar === ":") {
|
|
45401
|
+
return phase;
|
|
45402
|
+
}
|
|
45403
|
+
}
|
|
45404
|
+
// Analyze command content for hints (not script name, since we already checked patterns)
|
|
45405
|
+
const lowerCommand = command.toLowerCase();
|
|
45406
|
+
if (lowerCommand.includes("test") || lowerCommand.includes("vitest") || lowerCommand.includes("jest") || lowerCommand.includes("mocha") || lowerCommand.includes("ava")) {
|
|
45407
|
+
return "test";
|
|
45408
|
+
}
|
|
45409
|
+
if (lowerCommand.includes("build") || lowerCommand.includes("compile") || lowerCommand.includes("webpack") || lowerCommand.includes("rspack") || lowerCommand.includes("rollup") || lowerCommand.includes("vite build")) {
|
|
45410
|
+
return "build";
|
|
45411
|
+
}
|
|
45412
|
+
if (lowerCommand.includes("lint") || lowerCommand.includes("eslint") || lowerCommand.includes("biome") || lowerCommand.includes("tslint") || lowerCommand.includes("actionlint") || lowerCommand.includes("yamllint")) {
|
|
45413
|
+
return "lint";
|
|
45414
|
+
}
|
|
45415
|
+
if (lowerCommand.includes("prettier") || lowerCommand.includes("format")) {
|
|
45416
|
+
return "format";
|
|
45417
|
+
}
|
|
45418
|
+
if (lowerCommand.includes("audit") || lowerCommand.includes("snyk") || lowerCommand.includes("npm-audit")) {
|
|
45419
|
+
return "security";
|
|
45420
|
+
}
|
|
45421
|
+
return "unknown";
|
|
45422
|
+
}
|
|
45423
|
+
/**
|
|
45424
|
+
* Determines if a script is mandatory based on its phase
|
|
45425
|
+
*/ function isScriptMandatory(phase) {
|
|
45426
|
+
return [
|
|
45427
|
+
"test",
|
|
45428
|
+
"build",
|
|
45429
|
+
"lint",
|
|
45430
|
+
"format"
|
|
45431
|
+
].includes(phase);
|
|
45432
|
+
}
|
|
45433
|
+
|
|
45434
|
+
;// CONCATENATED MODULE: ./src/gateways/script-discovery-gateway.ts
|
|
45435
|
+
/**
|
|
45436
|
+
* Gateway for discovering scripts from package.json manifests
|
|
45437
|
+
*/
|
|
45438
|
+
|
|
45439
|
+
|
|
45440
|
+
|
|
45441
|
+
/**
|
|
45442
|
+
* File system implementation of script discovery gateway
|
|
45443
|
+
*/ class FileSystemScriptDiscoveryGateway {
|
|
45444
|
+
async discoverScripts(targetDir) {
|
|
45445
|
+
const packageJsonPath = (0,external_node_path_.join)(targetDir, "package.json");
|
|
45446
|
+
if (!await file_system_utils_fileExists(packageJsonPath)) {
|
|
45447
|
+
return [];
|
|
45448
|
+
}
|
|
45449
|
+
try {
|
|
45450
|
+
const content = await (0,promises_.readFile)(packageJsonPath, "utf-8");
|
|
45451
|
+
const packageJson = JSON.parse(content);
|
|
45452
|
+
if (!packageJson.scripts) {
|
|
45453
|
+
return [];
|
|
45454
|
+
}
|
|
45455
|
+
const scripts = [];
|
|
45456
|
+
for (const [name, command] of Object.entries(packageJson.scripts)){
|
|
45457
|
+
const phase = determineScriptPhase(name, command);
|
|
45458
|
+
const isMandatory = isScriptMandatory(phase);
|
|
45459
|
+
scripts.push({
|
|
45460
|
+
name,
|
|
45461
|
+
command,
|
|
45462
|
+
phase,
|
|
45463
|
+
isMandatory
|
|
45464
|
+
});
|
|
45465
|
+
}
|
|
45466
|
+
return scripts;
|
|
45467
|
+
} catch {
|
|
45468
|
+
// If package.json is malformed or cannot be parsed, return empty array
|
|
45469
|
+
return [];
|
|
45470
|
+
}
|
|
45471
|
+
}
|
|
45472
|
+
}
|
|
45473
|
+
/**
|
|
45474
|
+
* Creates and returns the default script discovery gateway
|
|
45475
|
+
*/ function createScriptDiscoveryGateway() {
|
|
45476
|
+
return new FileSystemScriptDiscoveryGateway();
|
|
45477
|
+
}
|
|
45478
|
+
|
|
44979
45479
|
;// CONCATENATED MODULE: ./src/gateways/skill-file-gateway.ts
|
|
44980
45480
|
/**
|
|
44981
45481
|
* Gateway for skill file system operations.
|
|
@@ -45015,6 +45515,160 @@ async function watchConfig(options) {
|
|
|
45015
45515
|
return new FileSystemSkillFileGateway();
|
|
45016
45516
|
}
|
|
45017
45517
|
|
|
45518
|
+
;// CONCATENATED MODULE: ./src/gateways/tool-discovery-gateway.ts
|
|
45519
|
+
/**
|
|
45520
|
+
* Gateway for discovering CLI tools and commands from GitHub Actions workflows
|
|
45521
|
+
*/
|
|
45522
|
+
|
|
45523
|
+
|
|
45524
|
+
|
|
45525
|
+
|
|
45526
|
+
/**
|
|
45527
|
+
* File system implementation of tool discovery gateway
|
|
45528
|
+
*/ class FileSystemToolDiscoveryGateway {
|
|
45529
|
+
async discoverTools(targetDir) {
|
|
45530
|
+
const workflowsDir = (0,external_node_path_.join)(targetDir, ".github", "workflows");
|
|
45531
|
+
if (!await file_system_utils_fileExists(workflowsDir)) {
|
|
45532
|
+
return [];
|
|
45533
|
+
}
|
|
45534
|
+
const files = await (0,promises_.readdir)(workflowsDir);
|
|
45535
|
+
const yamlFiles = files.filter((f)=>f.endsWith(".yml") || f.endsWith(".yaml"));
|
|
45536
|
+
const allTools = [];
|
|
45537
|
+
for (const file of yamlFiles){
|
|
45538
|
+
const filePath = (0,external_node_path_.join)(workflowsDir, file);
|
|
45539
|
+
try {
|
|
45540
|
+
const content = await (0,promises_.readFile)(filePath, "utf-8");
|
|
45541
|
+
const workflow = (0,yaml_dist.parse)(content);
|
|
45542
|
+
const tools = this.extractToolsFromWorkflow(workflow, file);
|
|
45543
|
+
allTools.push(...tools);
|
|
45544
|
+
} catch {
|
|
45545
|
+
// Skip files that can't be parsed as valid YAML
|
|
45546
|
+
}
|
|
45547
|
+
}
|
|
45548
|
+
return this.deduplicateTools(allTools);
|
|
45549
|
+
}
|
|
45550
|
+
extractToolsFromWorkflow(workflow, sourceFile) {
|
|
45551
|
+
const tools = [];
|
|
45552
|
+
if (!workflow || typeof workflow !== "object") {
|
|
45553
|
+
return tools;
|
|
45554
|
+
}
|
|
45555
|
+
const jobs = workflow.jobs;
|
|
45556
|
+
if (!jobs || typeof jobs !== "object") {
|
|
45557
|
+
return tools;
|
|
45558
|
+
}
|
|
45559
|
+
for (const job of Object.values(jobs)){
|
|
45560
|
+
if (!job || typeof job !== "object") {
|
|
45561
|
+
continue;
|
|
45562
|
+
}
|
|
45563
|
+
const steps = job.steps;
|
|
45564
|
+
if (!Array.isArray(steps)) {
|
|
45565
|
+
continue;
|
|
45566
|
+
}
|
|
45567
|
+
for (const step of steps){
|
|
45568
|
+
if (!step || typeof step !== "object") {
|
|
45569
|
+
continue;
|
|
45570
|
+
}
|
|
45571
|
+
const stepObj = step;
|
|
45572
|
+
const run = stepObj.run;
|
|
45573
|
+
if (typeof run === "string") {
|
|
45574
|
+
// Extract tools from run commands
|
|
45575
|
+
const extractedTools = this.extractToolsFromRunCommand(run, sourceFile);
|
|
45576
|
+
tools.push(...extractedTools);
|
|
45577
|
+
}
|
|
45578
|
+
}
|
|
45579
|
+
}
|
|
45580
|
+
return tools;
|
|
45581
|
+
}
|
|
45582
|
+
extractToolsFromRunCommand(runCommand, sourceFile) {
|
|
45583
|
+
const tools = [];
|
|
45584
|
+
// Split by newlines and pipe tokens with surrounding whitespace to handle
|
|
45585
|
+
// multi-line and piped commands without breaking on constructs like "cmd || true"
|
|
45586
|
+
const commands = runCommand.split(/\n|\s\|\s/).map((c)=>c.trim()).filter((c)=>c.length > 0 && !c.startsWith("#"));
|
|
45587
|
+
for (const command of commands){
|
|
45588
|
+
// Extract the base command (first word)
|
|
45589
|
+
const parts = command.split(/\s+/);
|
|
45590
|
+
const baseCommand = parts[0];
|
|
45591
|
+
// Skip shell built-ins and common utilities
|
|
45592
|
+
if (this.isShellBuiltin(baseCommand)) {
|
|
45593
|
+
continue;
|
|
45594
|
+
}
|
|
45595
|
+
// Determine tool name and full command
|
|
45596
|
+
let toolName;
|
|
45597
|
+
const fullCommand = command;
|
|
45598
|
+
// Handle special cases like "npm run", "mise run", etc.
|
|
45599
|
+
if (parts.length >= 2 && (baseCommand === "npm" || baseCommand === "mise") && parts[1] === "run") {
|
|
45600
|
+
if (parts.length >= 3 && parts[2]) {
|
|
45601
|
+
// "npm run test" -> name: "npm run test"
|
|
45602
|
+
toolName = parts.slice(0, 3).join(" ");
|
|
45603
|
+
} else {
|
|
45604
|
+
// Handle commands like "npm run" without a script name
|
|
45605
|
+
toolName = parts.slice(0, 2).join(" ");
|
|
45606
|
+
}
|
|
45607
|
+
} else if (parts.length >= 2) {
|
|
45608
|
+
// For most tools, include the subcommand for better specificity
|
|
45609
|
+
// e.g., "npm test", "npx biome", "pnpm lint"
|
|
45610
|
+
toolName = parts.slice(0, 2).join(" ");
|
|
45611
|
+
} else {
|
|
45612
|
+
// Fallback to the base command if no subcommand is present
|
|
45613
|
+
toolName = baseCommand;
|
|
45614
|
+
}
|
|
45615
|
+
const phase = this.determineToolPhase(toolName, fullCommand);
|
|
45616
|
+
const isMandatory = isScriptMandatory(phase);
|
|
45617
|
+
tools.push({
|
|
45618
|
+
name: toolName,
|
|
45619
|
+
fullCommand,
|
|
45620
|
+
phase,
|
|
45621
|
+
isMandatory,
|
|
45622
|
+
sourceWorkflow: sourceFile
|
|
45623
|
+
});
|
|
45624
|
+
}
|
|
45625
|
+
return tools;
|
|
45626
|
+
}
|
|
45627
|
+
isShellBuiltin(command) {
|
|
45628
|
+
const builtins = [
|
|
45629
|
+
"cd",
|
|
45630
|
+
"echo",
|
|
45631
|
+
"mkdir",
|
|
45632
|
+
"rm",
|
|
45633
|
+
"cp",
|
|
45634
|
+
"mv",
|
|
45635
|
+
"test",
|
|
45636
|
+
"[",
|
|
45637
|
+
"if",
|
|
45638
|
+
"then",
|
|
45639
|
+
"else",
|
|
45640
|
+
"fi",
|
|
45641
|
+
"for",
|
|
45642
|
+
"while",
|
|
45643
|
+
"do",
|
|
45644
|
+
"done",
|
|
45645
|
+
"case",
|
|
45646
|
+
"esac"
|
|
45647
|
+
];
|
|
45648
|
+
return builtins.includes(command);
|
|
45649
|
+
}
|
|
45650
|
+
determineToolPhase(toolName, fullCommand) {
|
|
45651
|
+
// Use the same logic as script phase determination
|
|
45652
|
+
return determineScriptPhase(toolName, fullCommand);
|
|
45653
|
+
}
|
|
45654
|
+
deduplicateTools(tools) {
|
|
45655
|
+
const seen = new Map();
|
|
45656
|
+
for (const tool of tools){
|
|
45657
|
+
// Use full command as key for deduplication
|
|
45658
|
+
const key = tool.fullCommand;
|
|
45659
|
+
if (!seen.has(key)) {
|
|
45660
|
+
seen.set(key, tool);
|
|
45661
|
+
}
|
|
45662
|
+
}
|
|
45663
|
+
return Array.from(seen.values());
|
|
45664
|
+
}
|
|
45665
|
+
}
|
|
45666
|
+
/**
|
|
45667
|
+
* Creates and returns the default tool discovery gateway
|
|
45668
|
+
*/ function createToolDiscoveryGateway() {
|
|
45669
|
+
return new FileSystemToolDiscoveryGateway();
|
|
45670
|
+
}
|
|
45671
|
+
|
|
45018
45672
|
;// CONCATENATED MODULE: ./src/use-cases/setup-step-discovery.ts
|
|
45019
45673
|
/**
|
|
45020
45674
|
* Use case for discovering setup steps in workflows.
|
|
@@ -45313,6 +45967,9 @@ async function watchConfig(options) {
|
|
|
45313
45967
|
|
|
45314
45968
|
|
|
45315
45969
|
|
|
45970
|
+
|
|
45971
|
+
|
|
45972
|
+
|
|
45316
45973
|
;// CONCATENATED MODULE: ./src/mcp/tools/types.ts
|
|
45317
45974
|
/**
|
|
45318
45975
|
* Shared types and utilities for MCP tool handlers.
|
|
@@ -45532,7 +46189,7 @@ Example resolved format: actions/setup-node@1a2b3c4d5e6f # v4.0.0`;
|
|
|
45532
46189
|
/**
|
|
45533
46190
|
* Use case for building setup step candidates from environment detection.
|
|
45534
46191
|
* This module handles the logic of determining which GitHub Actions
|
|
45535
|
-
* setup steps should be added based on detected version files.
|
|
46192
|
+
* setup steps should be added based on detected version files and package managers.
|
|
45536
46193
|
*/
|
|
45537
46194
|
|
|
45538
46195
|
/**
|
|
@@ -45557,7 +46214,12 @@ Example resolved format: actions/setup-node@1a2b3c4d5e6f # v4.0.0`;
|
|
|
45557
46214
|
return candidates;
|
|
45558
46215
|
}
|
|
45559
46216
|
// Otherwise, add individual setup actions for each version file
|
|
45560
|
-
|
|
46217
|
+
const setupCandidates = await buildCandidatesFromVersionFiles(environment.versionFiles, versionTypeToAction, versionFileConfigKeys, versionGateway);
|
|
46218
|
+
candidates.push(...setupCandidates);
|
|
46219
|
+
// Add install steps for detected package managers
|
|
46220
|
+
const installCandidates = buildInstallCandidatesFromPackageManagers(environment.packageManagers, loadedConfig);
|
|
46221
|
+
candidates.push(...installCandidates);
|
|
46222
|
+
return candidates;
|
|
45561
46223
|
}
|
|
45562
46224
|
/**
|
|
45563
46225
|
* Builds setup step candidates from individual version files
|
|
@@ -45592,6 +46254,59 @@ Example resolved format: actions/setup-node@1a2b3c4d5e6f # v4.0.0`;
|
|
|
45592
46254
|
}
|
|
45593
46255
|
return candidates;
|
|
45594
46256
|
}
|
|
46257
|
+
/**
|
|
46258
|
+
* Builds install step candidates from detected package managers
|
|
46259
|
+
* @param packageManagers Array of detected package managers
|
|
46260
|
+
* @param config Configuration for package manager mappings
|
|
46261
|
+
* @returns Array of install step candidates
|
|
46262
|
+
*/ function buildInstallCandidatesFromPackageManagers(packageManagers, config) {
|
|
46263
|
+
const candidates = [];
|
|
46264
|
+
const addedTypes = new Set();
|
|
46265
|
+
for (const pm of packageManagers){
|
|
46266
|
+
// Skip if we've already added this package manager type
|
|
46267
|
+
if (addedTypes.has(pm.type)) {
|
|
46268
|
+
continue;
|
|
46269
|
+
}
|
|
46270
|
+
addedTypes.add(pm.type);
|
|
46271
|
+
// Find the config for this package manager
|
|
46272
|
+
const pmConfig = config.packageManagers.find((c)=>c.type === pm.type);
|
|
46273
|
+
if (!pmConfig) {
|
|
46274
|
+
continue;
|
|
46275
|
+
}
|
|
46276
|
+
// Determine a descriptive name for the install step
|
|
46277
|
+
const stepName = getInstallStepName(pm.type);
|
|
46278
|
+
// Create install step candidate
|
|
46279
|
+
// Note: Empty action string indicates this is a run step (uses 'run' field instead of 'uses')
|
|
46280
|
+
// This is checked in workflow-generator.ts buildStepFromCandidate()
|
|
46281
|
+
candidates.push({
|
|
46282
|
+
action: "",
|
|
46283
|
+
source: "version-file",
|
|
46284
|
+
name: stepName,
|
|
46285
|
+
run: pmConfig.installCommand
|
|
46286
|
+
});
|
|
46287
|
+
}
|
|
46288
|
+
return candidates;
|
|
46289
|
+
}
|
|
46290
|
+
/**
|
|
46291
|
+
* Gets a descriptive name for an install step based on package manager type
|
|
46292
|
+
*/ function getInstallStepName(packageManagerType) {
|
|
46293
|
+
const names = {
|
|
46294
|
+
npm: "Install Node.js dependencies",
|
|
46295
|
+
yarn: "Install Node.js dependencies",
|
|
46296
|
+
pnpm: "Install Node.js dependencies",
|
|
46297
|
+
pip: "Install Python dependencies",
|
|
46298
|
+
pipenv: "Install Python dependencies",
|
|
46299
|
+
poetry: "Install Python dependencies",
|
|
46300
|
+
bundler: "Install Ruby dependencies",
|
|
46301
|
+
cargo: "Build Rust project",
|
|
46302
|
+
composer: "Install PHP dependencies",
|
|
46303
|
+
maven: "Install Java dependencies",
|
|
46304
|
+
gradle: "Build Gradle project",
|
|
46305
|
+
gomod: "Download Go dependencies",
|
|
46306
|
+
pub: "Install Dart dependencies"
|
|
46307
|
+
};
|
|
46308
|
+
return names[packageManagerType] || "Install dependencies";
|
|
46309
|
+
}
|
|
45595
46310
|
|
|
45596
46311
|
;// CONCATENATED MODULE: ./node_modules/@github-actions-workflow-ts/lib/dist/esm/workflow/index.js
|
|
45597
46312
|
/**
|
|
@@ -46037,11 +46752,22 @@ function getGlobalWacContext() {
|
|
|
46037
46752
|
* @param options Optional conversion options for placeholders and resolved versions
|
|
46038
46753
|
* @returns A typed Step object
|
|
46039
46754
|
*/ function buildStepFromCandidate(candidate, options) {
|
|
46755
|
+
// Handle run steps (install commands)
|
|
46756
|
+
// Run steps have a 'run' field and no action (or empty action string)
|
|
46757
|
+
if (candidate.run && !candidate.action) {
|
|
46758
|
+
const stepProps = {
|
|
46759
|
+
name: candidate.name || "Run command",
|
|
46760
|
+
run: candidate.run
|
|
46761
|
+
};
|
|
46762
|
+
// Type assertion is safe here - Step constructor accepts both 'uses' and 'run' at runtime
|
|
46763
|
+
return new Step(stepProps);
|
|
46764
|
+
}
|
|
46765
|
+
// Handle action steps (uses)
|
|
46040
46766
|
const version = getVersionForAction(candidate.action, candidate.version, options);
|
|
46041
46767
|
const usesValue = buildUsesValue(candidate.action, version, options);
|
|
46042
46768
|
const withConfig = candidate.config && Object.keys(candidate.config).length > 0 ? candidate.config : undefined;
|
|
46043
46769
|
const stepProps = {
|
|
46044
|
-
name: generateStepName(candidate.action),
|
|
46770
|
+
name: candidate.name || generateStepName(candidate.action),
|
|
46045
46771
|
uses: usesValue,
|
|
46046
46772
|
with: withConfig
|
|
46047
46773
|
};
|
|
@@ -46319,6 +47045,140 @@ function getGlobalWacContext() {
|
|
|
46319
47045
|
});
|
|
46320
47046
|
};
|
|
46321
47047
|
|
|
47048
|
+
;// CONCATENATED MODULE: ./src/use-cases/discover-feedback-loops.ts
|
|
47049
|
+
/**
|
|
47050
|
+
* Use case for discovering and analyzing SDLC feedback loops
|
|
47051
|
+
*/ /**
|
|
47052
|
+
* Use case for discovering scripts and tools that form SDLC feedback loops
|
|
47053
|
+
*/ class DiscoverFeedbackLoopsUseCase {
|
|
47054
|
+
scriptGateway;
|
|
47055
|
+
toolGateway;
|
|
47056
|
+
packageManagerGateway;
|
|
47057
|
+
constructor(scriptGateway, toolGateway, packageManagerGateway){
|
|
47058
|
+
this.scriptGateway = scriptGateway;
|
|
47059
|
+
this.toolGateway = toolGateway;
|
|
47060
|
+
this.packageManagerGateway = packageManagerGateway;
|
|
47061
|
+
}
|
|
47062
|
+
async execute(input) {
|
|
47063
|
+
if (!input.targetDir) {
|
|
47064
|
+
throw new Error("Target directory is required");
|
|
47065
|
+
}
|
|
47066
|
+
// Discover scripts from package.json
|
|
47067
|
+
const scripts = await this.scriptGateway.discoverScripts(input.targetDir);
|
|
47068
|
+
// Discover tools from GitHub Actions workflows
|
|
47069
|
+
const tools = await this.toolGateway.discoverTools(input.targetDir);
|
|
47070
|
+
// Detect package manager
|
|
47071
|
+
const packageManagers = await this.packageManagerGateway.detectPackageManagers(input.targetDir);
|
|
47072
|
+
const primaryPackageManager = packageManagers.length > 0 ? packageManagers[0].type : undefined;
|
|
47073
|
+
const feedbackLoops = {
|
|
47074
|
+
scripts: this.sortByPhase(scripts),
|
|
47075
|
+
tools: this.sortByPhase(tools),
|
|
47076
|
+
packageManager: primaryPackageManager
|
|
47077
|
+
};
|
|
47078
|
+
return {
|
|
47079
|
+
feedbackLoops
|
|
47080
|
+
};
|
|
47081
|
+
}
|
|
47082
|
+
/**
|
|
47083
|
+
* Sorts scripts or tools by phase priority
|
|
47084
|
+
* Order: test, lint, format, build, security, install, dev, deploy, unknown
|
|
47085
|
+
*/ sortByPhase(items) {
|
|
47086
|
+
const phasePriority = {
|
|
47087
|
+
test: 1,
|
|
47088
|
+
lint: 2,
|
|
47089
|
+
format: 3,
|
|
47090
|
+
build: 4,
|
|
47091
|
+
security: 5,
|
|
47092
|
+
install: 6,
|
|
47093
|
+
dev: 7,
|
|
47094
|
+
deploy: 8,
|
|
47095
|
+
unknown: 9
|
|
47096
|
+
};
|
|
47097
|
+
return [
|
|
47098
|
+
...items
|
|
47099
|
+
].sort((a, b)=>{
|
|
47100
|
+
const priorityA = phasePriority[a.phase] ?? 10;
|
|
47101
|
+
const priorityB = phasePriority[b.phase] ?? 10;
|
|
47102
|
+
return priorityA - priorityB;
|
|
47103
|
+
});
|
|
47104
|
+
}
|
|
47105
|
+
}
|
|
47106
|
+
|
|
47107
|
+
;// CONCATENATED MODULE: ./src/mcp/tools/discover-feedback-loops.ts
|
|
47108
|
+
/**
|
|
47109
|
+
* MCP tool handler for discovering scripts and CLI tools.
|
|
47110
|
+
*/
|
|
47111
|
+
|
|
47112
|
+
|
|
47113
|
+
|
|
47114
|
+
|
|
47115
|
+
/**
|
|
47116
|
+
* Discovers npm scripts from package.json and CLI tools from GitHub Actions workflows,
|
|
47117
|
+
* mapping them to SDLC feedback loop phases (test, build, lint, format, etc.).
|
|
47118
|
+
*/ const discoverFeedbackLoopsHandler = async (args)=>{
|
|
47119
|
+
const dir = args.targetDir || process.cwd();
|
|
47120
|
+
try {
|
|
47121
|
+
// Create gateways
|
|
47122
|
+
const scriptGateway = createScriptDiscoveryGateway();
|
|
47123
|
+
const toolGateway = createToolDiscoveryGateway();
|
|
47124
|
+
const environmentGateway = createEnvironmentGateway();
|
|
47125
|
+
// Adapter for package manager gateway
|
|
47126
|
+
const packageManagerGateway = {
|
|
47127
|
+
async detectPackageManagers (targetDir) {
|
|
47128
|
+
const env = await environmentGateway.detectEnvironment(targetDir);
|
|
47129
|
+
return env.packageManagers;
|
|
47130
|
+
}
|
|
47131
|
+
};
|
|
47132
|
+
// Create use case
|
|
47133
|
+
const useCase = new DiscoverFeedbackLoopsUseCase(scriptGateway, toolGateway, packageManagerGateway);
|
|
47134
|
+
// Execute discovery
|
|
47135
|
+
const result = await useCase.execute({
|
|
47136
|
+
targetDir: dir
|
|
47137
|
+
});
|
|
47138
|
+
const { scripts, tools, packageManager } = result.feedbackLoops;
|
|
47139
|
+
// Group by phase for better readability
|
|
47140
|
+
const scriptsByPhase = scripts.reduce((acc, script)=>{
|
|
47141
|
+
if (!acc[script.phase]) {
|
|
47142
|
+
acc[script.phase] = [];
|
|
47143
|
+
}
|
|
47144
|
+
acc[script.phase].push({
|
|
47145
|
+
name: script.name,
|
|
47146
|
+
command: script.command,
|
|
47147
|
+
mandatory: script.isMandatory
|
|
47148
|
+
});
|
|
47149
|
+
return acc;
|
|
47150
|
+
}, {});
|
|
47151
|
+
const toolsByPhase = tools.reduce((acc, tool)=>{
|
|
47152
|
+
if (!acc[tool.phase]) {
|
|
47153
|
+
acc[tool.phase] = [];
|
|
47154
|
+
}
|
|
47155
|
+
acc[tool.phase].push({
|
|
47156
|
+
name: tool.name,
|
|
47157
|
+
command: tool.fullCommand,
|
|
47158
|
+
mandatory: tool.isMandatory,
|
|
47159
|
+
source: tool.sourceWorkflow
|
|
47160
|
+
});
|
|
47161
|
+
return acc;
|
|
47162
|
+
}, {});
|
|
47163
|
+
const mandatoryScripts = scripts.filter((s)=>s.isMandatory);
|
|
47164
|
+
const mandatoryTools = tools.filter((t)=>t.isMandatory);
|
|
47165
|
+
return successResponse({
|
|
47166
|
+
summary: {
|
|
47167
|
+
totalScripts: scripts.length,
|
|
47168
|
+
totalTools: tools.length,
|
|
47169
|
+
mandatoryScripts: mandatoryScripts.length,
|
|
47170
|
+
mandatoryTools: mandatoryTools.length,
|
|
47171
|
+
packageManager: packageManager || "none detected"
|
|
47172
|
+
},
|
|
47173
|
+
scriptsByPhase,
|
|
47174
|
+
toolsByPhase,
|
|
47175
|
+
message: `Discovered ${scripts.length} package.json script(s) and ${tools.length} CLI tool(s) from workflows`
|
|
47176
|
+
});
|
|
47177
|
+
} catch (error) {
|
|
47178
|
+
return types_errorResponse(`Failed to discover feedback loops: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
47179
|
+
}
|
|
47180
|
+
};
|
|
47181
|
+
|
|
46322
47182
|
;// CONCATENATED MODULE: ./src/mcp/tools/discover-workflow-setup-actions.ts
|
|
46323
47183
|
/**
|
|
46324
47184
|
* MCP tool handler for discovering setup actions in existing workflows.
|
|
@@ -46438,6 +47298,162 @@ function getGlobalWacContext() {
|
|
|
46438
47298
|
});
|
|
46439
47299
|
};
|
|
46440
47300
|
|
|
47301
|
+
;// CONCATENATED MODULE: ./src/use-cases/validate-instruction-coverage.ts
|
|
47302
|
+
/**
|
|
47303
|
+
* Use case for validating instruction coverage of feedback loops
|
|
47304
|
+
*/ /**
|
|
47305
|
+
* Use case for validating that repository instructions cover mandatory feedback loops
|
|
47306
|
+
*/ class ValidateInstructionCoverageUseCase {
|
|
47307
|
+
discoverFeedbackLoops;
|
|
47308
|
+
instructionGateway;
|
|
47309
|
+
constructor(discoverFeedbackLoops, instructionGateway){
|
|
47310
|
+
this.discoverFeedbackLoops = discoverFeedbackLoops;
|
|
47311
|
+
this.instructionGateway = instructionGateway;
|
|
47312
|
+
}
|
|
47313
|
+
async execute(input) {
|
|
47314
|
+
if (!input.targetDir) {
|
|
47315
|
+
throw new Error("Target directory is required");
|
|
47316
|
+
}
|
|
47317
|
+
// First, discover all feedback loops
|
|
47318
|
+
const discoveryResult = await this.discoverFeedbackLoops.execute({
|
|
47319
|
+
targetDir: input.targetDir
|
|
47320
|
+
});
|
|
47321
|
+
const { scripts, tools, packageManager } = discoveryResult.feedbackLoops;
|
|
47322
|
+
// Analyze instruction coverage
|
|
47323
|
+
const coverage = await this.instructionGateway.analyzeCoverage(input.targetDir, scripts, tools);
|
|
47324
|
+
// Generate suggestions for missing documentation
|
|
47325
|
+
const suggestions = this.generateSuggestions(coverage, packageManager || "npm");
|
|
47326
|
+
return {
|
|
47327
|
+
coverage,
|
|
47328
|
+
hasFullCoverage: coverage.summary.coveragePercentage === 100,
|
|
47329
|
+
suggestions
|
|
47330
|
+
};
|
|
47331
|
+
}
|
|
47332
|
+
generateSuggestions(coverage, packageManager) {
|
|
47333
|
+
const suggestions = [];
|
|
47334
|
+
if (coverage.missingInInstructions.length === 0) {
|
|
47335
|
+
suggestions.push("✅ All mandatory feedback loops are documented in instructions");
|
|
47336
|
+
return suggestions;
|
|
47337
|
+
}
|
|
47338
|
+
suggestions.push(`⚠️ ${coverage.missingInInstructions.length} mandatory feedback loop(s) are not documented:`);
|
|
47339
|
+
suggestions.push("");
|
|
47340
|
+
// Group by phase
|
|
47341
|
+
const byPhase = new Map();
|
|
47342
|
+
for (const item of coverage.missingInInstructions){
|
|
47343
|
+
const existing = byPhase.get(item.phase) || [];
|
|
47344
|
+
existing.push(item);
|
|
47345
|
+
byPhase.set(item.phase, existing);
|
|
47346
|
+
}
|
|
47347
|
+
// Generate suggestions per phase
|
|
47348
|
+
for (const [phase, items] of byPhase.entries()){
|
|
47349
|
+
suggestions.push(`${phase.toUpperCase()} phase:`);
|
|
47350
|
+
for (const item of items){
|
|
47351
|
+
if ("command" in item) {
|
|
47352
|
+
// It's a script
|
|
47353
|
+
suggestions.push(` - Document "${packageManager} run ${item.name}" (runs: ${item.command})`);
|
|
47354
|
+
} else {
|
|
47355
|
+
// It's a tool
|
|
47356
|
+
suggestions.push(` - Document "${item.fullCommand}"`);
|
|
47357
|
+
}
|
|
47358
|
+
}
|
|
47359
|
+
suggestions.push("");
|
|
47360
|
+
}
|
|
47361
|
+
suggestions.push("Consider adding these to .github/copilot-instructions.md");
|
|
47362
|
+
suggestions.push("or creating dedicated instruction files in .github/instructions/");
|
|
47363
|
+
return suggestions;
|
|
47364
|
+
}
|
|
47365
|
+
}
|
|
47366
|
+
|
|
47367
|
+
;// CONCATENATED MODULE: ./src/mcp/tools/validate-instruction-coverage.ts
|
|
47368
|
+
/**
|
|
47369
|
+
* MCP tool handler for validating instruction coverage of feedback loops.
|
|
47370
|
+
*/
|
|
47371
|
+
|
|
47372
|
+
|
|
47373
|
+
|
|
47374
|
+
|
|
47375
|
+
|
|
47376
|
+
|
|
47377
|
+
/**
|
|
47378
|
+
* Validates that repository instructions (.github/copilot-instructions.md and .github/instructions/*.md)
|
|
47379
|
+
* document all mandatory feedback loop scripts and tools.
|
|
47380
|
+
* Returns coverage percentage and suggestions for missing documentation.
|
|
47381
|
+
*/ const validateInstructionCoverageHandler = async (args)=>{
|
|
47382
|
+
const dir = args.targetDir || process.cwd();
|
|
47383
|
+
try {
|
|
47384
|
+
// Create gateways
|
|
47385
|
+
const scriptGateway = createScriptDiscoveryGateway();
|
|
47386
|
+
const toolGateway = createToolDiscoveryGateway();
|
|
47387
|
+
const environmentGateway = createEnvironmentGateway();
|
|
47388
|
+
const instructionGateway = createInstructionAnalysisGateway();
|
|
47389
|
+
// Adapter for package manager gateway
|
|
47390
|
+
const packageManagerGateway = {
|
|
47391
|
+
async detectPackageManagers (targetDir) {
|
|
47392
|
+
const env = await environmentGateway.detectEnvironment(targetDir);
|
|
47393
|
+
return env.packageManagers;
|
|
47394
|
+
}
|
|
47395
|
+
};
|
|
47396
|
+
// Create use cases
|
|
47397
|
+
const discoverUseCase = new DiscoverFeedbackLoopsUseCase(scriptGateway, toolGateway, packageManagerGateway);
|
|
47398
|
+
const validateUseCase = new ValidateInstructionCoverageUseCase(discoverUseCase, instructionGateway);
|
|
47399
|
+
// Execute validation
|
|
47400
|
+
const result = await validateUseCase.execute({
|
|
47401
|
+
targetDir: dir
|
|
47402
|
+
});
|
|
47403
|
+
const { coverage, hasFullCoverage, suggestions } = result;
|
|
47404
|
+
// Format missing items for better readability
|
|
47405
|
+
const missing = coverage.missingInInstructions.map((item)=>{
|
|
47406
|
+
if ("command" in item) {
|
|
47407
|
+
// Script
|
|
47408
|
+
return {
|
|
47409
|
+
type: "script",
|
|
47410
|
+
name: item.name,
|
|
47411
|
+
phase: item.phase,
|
|
47412
|
+
command: item.command
|
|
47413
|
+
};
|
|
47414
|
+
}
|
|
47415
|
+
// Tool
|
|
47416
|
+
return {
|
|
47417
|
+
type: "tool",
|
|
47418
|
+
name: item.name,
|
|
47419
|
+
phase: item.phase,
|
|
47420
|
+
command: item.fullCommand
|
|
47421
|
+
};
|
|
47422
|
+
});
|
|
47423
|
+
// Format documented items
|
|
47424
|
+
const documented = coverage.documentedInInstructions.map((item)=>{
|
|
47425
|
+
if ("command" in item) {
|
|
47426
|
+
// Script
|
|
47427
|
+
return {
|
|
47428
|
+
type: "script",
|
|
47429
|
+
name: item.name,
|
|
47430
|
+
phase: item.phase
|
|
47431
|
+
};
|
|
47432
|
+
}
|
|
47433
|
+
// Tool
|
|
47434
|
+
return {
|
|
47435
|
+
type: "tool",
|
|
47436
|
+
name: item.name,
|
|
47437
|
+
phase: item.phase
|
|
47438
|
+
};
|
|
47439
|
+
});
|
|
47440
|
+
return successResponse({
|
|
47441
|
+
hasFullCoverage,
|
|
47442
|
+
summary: {
|
|
47443
|
+
totalMandatory: coverage.summary.totalMandatory,
|
|
47444
|
+
totalDocumented: coverage.summary.totalDocumented,
|
|
47445
|
+
coveragePercentage: coverage.summary.coveragePercentage
|
|
47446
|
+
},
|
|
47447
|
+
missing,
|
|
47448
|
+
documented,
|
|
47449
|
+
suggestions,
|
|
47450
|
+
message: hasFullCoverage ? "✅ All mandatory feedback loops are documented" : `⚠️ ${coverage.summary.coveragePercentage.toFixed(1)}% coverage - ${missing.length} mandatory items not documented`
|
|
47451
|
+
});
|
|
47452
|
+
} catch (error) {
|
|
47453
|
+
return types_errorResponse(`Failed to validate instruction coverage: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
47454
|
+
}
|
|
47455
|
+
};
|
|
47456
|
+
|
|
46441
47457
|
;// CONCATENATED MODULE: ./src/mcp/tools/index.ts
|
|
46442
47458
|
/**
|
|
46443
47459
|
* MCP tool handlers module exports.
|
|
@@ -46449,6 +47465,8 @@ function getGlobalWacContext() {
|
|
|
46449
47465
|
|
|
46450
47466
|
|
|
46451
47467
|
|
|
47468
|
+
|
|
47469
|
+
|
|
46452
47470
|
;// CONCATENATED MODULE: ./src/mcp/server.ts
|
|
46453
47471
|
/**
|
|
46454
47472
|
* MCP Server for Copilot Setup Steps workflow management.
|
|
@@ -46500,6 +47518,18 @@ function getGlobalWacContext() {
|
|
|
46500
47518
|
handler: discoverWorkflowSetupActionsHandler,
|
|
46501
47519
|
inputSchema: targetDirInputSchema
|
|
46502
47520
|
},
|
|
47521
|
+
{
|
|
47522
|
+
name: "discover_feedback_loops",
|
|
47523
|
+
description: "Discover npm scripts from package.json and CLI tools from GitHub Actions workflows, mapping them to SDLC feedback loop phases (test, build, lint, format, security, etc.)",
|
|
47524
|
+
handler: discoverFeedbackLoopsHandler,
|
|
47525
|
+
inputSchema: targetDirInputSchema
|
|
47526
|
+
},
|
|
47527
|
+
{
|
|
47528
|
+
name: "validate_instruction_coverage",
|
|
47529
|
+
description: "Validate that repository instructions (.github/copilot-instructions.md and .github/instructions/*.md) document all mandatory feedback loop scripts and tools. Returns coverage percentage and suggestions for missing documentation.",
|
|
47530
|
+
handler: validateInstructionCoverageHandler,
|
|
47531
|
+
inputSchema: targetDirInputSchema
|
|
47532
|
+
},
|
|
46503
47533
|
{
|
|
46504
47534
|
name: "read_copilot_setup_workflow",
|
|
46505
47535
|
description: "Read the existing Copilot Setup Steps workflow (copilot-setup-steps.yml or .yaml) from a target directory",
|