@lousy-agents/cli 2.4.0 → 2.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/dist/index.js +343 -18
- package/dist/index.js.map +1 -1
- package/dist/mcp-server.js +751 -4
- package/dist/mcp-server.js.map +1 -1
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -126,8 +126,10 @@ npx @lousy-agents/cli copilot-setup
|
|
|
126
126
|
|---------|--------|
|
|
127
127
|
| Scaffolding for webapps | ✅ Complete |
|
|
128
128
|
| Scaffolding for REST APIs | ✅ Complete |
|
|
129
|
-
| Scaffolding for CLI |
|
|
129
|
+
| Scaffolding for CLI | ✅ Complete |
|
|
130
130
|
| Scaffolding for GraphQL APIs | Not Started |
|
|
131
|
+
| Copilot setup package manager install steps | ✅ Complete |
|
|
132
|
+
| Copilot agent and skill scaffolding | ✅ Complete |
|
|
131
133
|
| MCP server package | ✅ Complete |
|
|
132
134
|
|
|
133
135
|
## Documentation
|
package/dist/index.js
CHANGED
|
@@ -10032,7 +10032,7 @@ function resolveArgs(argsDef) {
|
|
|
10032
10032
|
function defineCommand(def) {
|
|
10033
10033
|
return def;
|
|
10034
10034
|
}
|
|
10035
|
-
async function
|
|
10035
|
+
async function dist_runCommand(cmd, opts) {
|
|
10036
10036
|
const cmdArgs = await resolveValue(cmd.args || {});
|
|
10037
10037
|
const parsedArgs = parseArgs(opts.rawArgs, cmdArgs);
|
|
10038
10038
|
const context = {
|
|
@@ -10051,7 +10051,7 @@ async function runCommand(cmd, opts) {
|
|
|
10051
10051
|
if (subCommandName) {
|
|
10052
10052
|
if (!subCommands[subCommandName]) throw new CLIError(`Unknown command ${cyan(subCommandName)}`, "E_UNKNOWN_COMMAND");
|
|
10053
10053
|
const subCommand = await resolveValue(subCommands[subCommandName]);
|
|
10054
|
-
if (subCommand) await
|
|
10054
|
+
if (subCommand) await dist_runCommand(subCommand, { rawArgs: opts.rawArgs.slice(subCommandArgIndex + 1) });
|
|
10055
10055
|
} else if (!cmd.run) throw new CLIError(`No command specified.`, "E_NO_COMMAND");
|
|
10056
10056
|
}
|
|
10057
10057
|
if (typeof cmd.run === "function") result = await cmd.run(context);
|
|
@@ -10151,7 +10151,7 @@ async function runMain(cmd, opts = {}) {
|
|
|
10151
10151
|
const meta = typeof cmd.meta === "function" ? await cmd.meta() : await cmd.meta;
|
|
10152
10152
|
if (!meta?.version) throw new CLIError("No version specified", "E_NO_VERSION");
|
|
10153
10153
|
console.log(meta.version);
|
|
10154
|
-
} else await
|
|
10154
|
+
} else await dist_runCommand(cmd, { rawArgs });
|
|
10155
10155
|
} catch (error) {
|
|
10156
10156
|
if (error instanceof CLIError) {
|
|
10157
10157
|
await showUsage$1(...await resolveSubCommand(cmd, rawArgs));
|
|
@@ -11754,7 +11754,7 @@ const consola = dist_createConsola();
|
|
|
11754
11754
|
*/
|
|
11755
11755
|
/**
|
|
11756
11756
|
* Checks if a file or directory exists
|
|
11757
|
-
*/ async function
|
|
11757
|
+
*/ async function file_system_utils_fileExists(path) {
|
|
11758
11758
|
try {
|
|
11759
11759
|
await (0,promises_.access)(path);
|
|
11760
11760
|
return true;
|
|
@@ -11778,7 +11778,7 @@ const consola = dist_createConsola();
|
|
|
11778
11778
|
}
|
|
11779
11779
|
async agentFileExists(targetDir, agentName) {
|
|
11780
11780
|
const filePath = this.getAgentFilePath(targetDir, agentName);
|
|
11781
|
-
return
|
|
11781
|
+
return file_system_utils_fileExists(filePath);
|
|
11782
11782
|
}
|
|
11783
11783
|
async ensureAgentsDirectory(targetDir) {
|
|
11784
11784
|
const agentsDir = (0,external_node_path_.join)(targetDir, ".github", "agents");
|
|
@@ -14291,14 +14291,14 @@ async function watchConfig(options) {
|
|
|
14291
14291
|
}
|
|
14292
14292
|
async detectMise(targetDir) {
|
|
14293
14293
|
const miseTomlPath = (0,external_node_path_.join)(targetDir, "mise.toml");
|
|
14294
|
-
return
|
|
14294
|
+
return file_system_utils_fileExists(miseTomlPath);
|
|
14295
14295
|
}
|
|
14296
14296
|
async detectVersionFiles(targetDir, config) {
|
|
14297
14297
|
const filenameToType = getVersionFilenameToTypeMap(config);
|
|
14298
14298
|
const versionFiles = [];
|
|
14299
14299
|
for (const fileConfig of config.versionFiles){
|
|
14300
14300
|
const filePath = (0,external_node_path_.join)(targetDir, fileConfig.filename);
|
|
14301
|
-
if (await
|
|
14301
|
+
if (await file_system_utils_fileExists(filePath)) {
|
|
14302
14302
|
const version = await readVersionFileContent(filePath);
|
|
14303
14303
|
versionFiles.push({
|
|
14304
14304
|
type: filenameToType[fileConfig.filename],
|
|
@@ -14333,7 +14333,7 @@ async function watchConfig(options) {
|
|
|
14333
14333
|
}
|
|
14334
14334
|
async detectNodePackageManager(targetDir, nodePackageManagers) {
|
|
14335
14335
|
const packageJsonPath = (0,external_node_path_.join)(targetDir, "package.json");
|
|
14336
|
-
if (!await
|
|
14336
|
+
if (!await file_system_utils_fileExists(packageJsonPath)) {
|
|
14337
14337
|
return null;
|
|
14338
14338
|
}
|
|
14339
14339
|
// Priority order for Node.js package managers: npm > yarn > pnpm
|
|
@@ -14348,7 +14348,7 @@ async function watchConfig(options) {
|
|
|
14348
14348
|
continue;
|
|
14349
14349
|
}
|
|
14350
14350
|
const lockfilePath = (0,external_node_path_.join)(targetDir, pmConfig.lockfile);
|
|
14351
|
-
if (await
|
|
14351
|
+
if (await file_system_utils_fileExists(lockfilePath)) {
|
|
14352
14352
|
return {
|
|
14353
14353
|
type: pmConfig.type,
|
|
14354
14354
|
filename: pmConfig.manifestFile,
|
|
@@ -14375,9 +14375,9 @@ async function watchConfig(options) {
|
|
|
14375
14375
|
continue;
|
|
14376
14376
|
}
|
|
14377
14377
|
const manifestPath = (0,external_node_path_.join)(targetDir, pmConfig.manifestFile);
|
|
14378
|
-
if (await
|
|
14378
|
+
if (await file_system_utils_fileExists(manifestPath)) {
|
|
14379
14379
|
const lockfilePath = pmConfig.lockfile ? (0,external_node_path_.join)(targetDir, pmConfig.lockfile) : undefined;
|
|
14380
|
-
const hasLockfile = lockfilePath ? await
|
|
14380
|
+
const hasLockfile = lockfilePath ? await file_system_utils_fileExists(lockfilePath) : false;
|
|
14381
14381
|
// Skip if lockfile is required but not present
|
|
14382
14382
|
if (pmConfig.requiresLockfile && !hasLockfile) {
|
|
14383
14383
|
continue;
|
|
@@ -14395,9 +14395,9 @@ async function watchConfig(options) {
|
|
|
14395
14395
|
const packageManagers = [];
|
|
14396
14396
|
for (const pmConfig of otherPackageManagers){
|
|
14397
14397
|
const manifestPath = (0,external_node_path_.join)(targetDir, pmConfig.manifestFile);
|
|
14398
|
-
if (await
|
|
14398
|
+
if (await file_system_utils_fileExists(manifestPath)) {
|
|
14399
14399
|
const lockfilePath = pmConfig.lockfile ? (0,external_node_path_.join)(targetDir, pmConfig.lockfile) : undefined;
|
|
14400
|
-
const hasLockfile = lockfilePath ? await
|
|
14400
|
+
const hasLockfile = lockfilePath ? await file_system_utils_fileExists(lockfilePath) : false;
|
|
14401
14401
|
// Skip if lockfile is required but not present
|
|
14402
14402
|
if (pmConfig.requiresLockfile && !hasLockfile) {
|
|
14403
14403
|
continue;
|
|
@@ -14418,6 +14418,174 @@ async function watchConfig(options) {
|
|
|
14418
14418
|
return new FileSystemEnvironmentGateway();
|
|
14419
14419
|
}
|
|
14420
14420
|
|
|
14421
|
+
;// CONCATENATED MODULE: ./src/gateways/instruction-analysis-gateway.ts
|
|
14422
|
+
/**
|
|
14423
|
+
* Gateway for analyzing repository instructions for feedback loop coverage
|
|
14424
|
+
*/
|
|
14425
|
+
|
|
14426
|
+
|
|
14427
|
+
/**
|
|
14428
|
+
* File system implementation of instruction analysis gateway
|
|
14429
|
+
*/ class FileSystemInstructionAnalysisGateway {
|
|
14430
|
+
async analyzeCoverage(targetDir, scripts, tools) {
|
|
14431
|
+
// Find all instruction files
|
|
14432
|
+
const instructionFiles = await this.findInstructionFiles(targetDir);
|
|
14433
|
+
// Read and search instruction content
|
|
14434
|
+
const references = [];
|
|
14435
|
+
const documentedTargets = new Set();
|
|
14436
|
+
for (const file of instructionFiles){
|
|
14437
|
+
const content = await readFile(file, "utf-8");
|
|
14438
|
+
const lines = content.split("\n");
|
|
14439
|
+
// Check for script references (e.g., "npm test", "npm run build")
|
|
14440
|
+
for (const script of scripts){
|
|
14441
|
+
const scriptRefs = this.findReferencesInContent(script.name, content, lines, file, targetDir);
|
|
14442
|
+
if (scriptRefs.length > 0) {
|
|
14443
|
+
references.push(...scriptRefs);
|
|
14444
|
+
documentedTargets.add(script.name);
|
|
14445
|
+
}
|
|
14446
|
+
}
|
|
14447
|
+
// Check for tool references (e.g., "mise run test", "biome check")
|
|
14448
|
+
for (const tool of tools){
|
|
14449
|
+
const toolRefs = this.findReferencesInContent(tool.name, content, lines, file, targetDir);
|
|
14450
|
+
if (toolRefs.length > 0) {
|
|
14451
|
+
references.push(...toolRefs);
|
|
14452
|
+
documentedTargets.add(tool.name);
|
|
14453
|
+
}
|
|
14454
|
+
}
|
|
14455
|
+
}
|
|
14456
|
+
// Filter mandatory scripts/tools
|
|
14457
|
+
const mandatoryScripts = scripts.filter((s)=>s.isMandatory);
|
|
14458
|
+
const mandatoryTools = tools.filter((t)=>t.isMandatory);
|
|
14459
|
+
const allMandatory = [
|
|
14460
|
+
...mandatoryScripts,
|
|
14461
|
+
...mandatoryTools
|
|
14462
|
+
];
|
|
14463
|
+
// Categorize as missing or documented
|
|
14464
|
+
const missingInInstructions = allMandatory.filter((item)=>!documentedTargets.has(item.name));
|
|
14465
|
+
const documentedInInstructions = allMandatory.filter((item)=>documentedTargets.has(item.name));
|
|
14466
|
+
const totalMandatory = allMandatory.length;
|
|
14467
|
+
const totalDocumented = documentedInInstructions.length;
|
|
14468
|
+
const coveragePercentage = totalMandatory === 0 ? 100 : totalDocumented / totalMandatory * 100;
|
|
14469
|
+
return {
|
|
14470
|
+
missingInInstructions,
|
|
14471
|
+
documentedInInstructions,
|
|
14472
|
+
references,
|
|
14473
|
+
summary: {
|
|
14474
|
+
totalMandatory,
|
|
14475
|
+
totalDocumented,
|
|
14476
|
+
coveragePercentage: Math.round(coveragePercentage * 100) / 100
|
|
14477
|
+
}
|
|
14478
|
+
};
|
|
14479
|
+
}
|
|
14480
|
+
async findInstructionFiles(targetDir) {
|
|
14481
|
+
const files = [];
|
|
14482
|
+
// Check for .github/copilot-instructions.md
|
|
14483
|
+
const copilotInstructions = join(targetDir, ".github", "copilot-instructions.md");
|
|
14484
|
+
if (await fileExists(copilotInstructions)) {
|
|
14485
|
+
files.push(copilotInstructions);
|
|
14486
|
+
}
|
|
14487
|
+
// Check for .github/instructions/*.md
|
|
14488
|
+
const instructionsDir = join(targetDir, ".github", "instructions");
|
|
14489
|
+
if (await fileExists(instructionsDir)) {
|
|
14490
|
+
try {
|
|
14491
|
+
const instructionFiles = await readdir(instructionsDir);
|
|
14492
|
+
for (const file of instructionFiles){
|
|
14493
|
+
if (file.endsWith(".md")) {
|
|
14494
|
+
files.push(join(instructionsDir, file));
|
|
14495
|
+
}
|
|
14496
|
+
}
|
|
14497
|
+
} catch {
|
|
14498
|
+
// Skip directory if we can't read it (e.g., permissions issues)
|
|
14499
|
+
// Similar to how workflow parsing errors are handled
|
|
14500
|
+
}
|
|
14501
|
+
}
|
|
14502
|
+
return files;
|
|
14503
|
+
}
|
|
14504
|
+
findReferencesInContent(target, content, lines, file, targetDir) {
|
|
14505
|
+
const references = [];
|
|
14506
|
+
// Build a case-insensitive, word-boundary-aware pattern for the target.
|
|
14507
|
+
// This reduces false positives from simple substring matches like
|
|
14508
|
+
// "test" in "testing" or "latest", while still matching common
|
|
14509
|
+
// separators such as spaces, punctuation, etc.
|
|
14510
|
+
const escapedTarget = target.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
14511
|
+
const targetPattern = new RegExp(`(?:^|[^\\w])(${escapedTarget})(?=$|[^\\w])`, "i");
|
|
14512
|
+
// Fast path: skip line-by-line processing if the target pattern
|
|
14513
|
+
// never appears in the full content.
|
|
14514
|
+
if (!targetPattern.test(content)) {
|
|
14515
|
+
return references;
|
|
14516
|
+
}
|
|
14517
|
+
// Find line numbers where target appears
|
|
14518
|
+
for(let i = 0; i < lines.length; i++){
|
|
14519
|
+
const line = lines[i];
|
|
14520
|
+
if (targetPattern.test(line)) {
|
|
14521
|
+
// Get context (line before and after if available)
|
|
14522
|
+
const contextLines = [];
|
|
14523
|
+
if (i > 0) contextLines.push(lines[i - 1]);
|
|
14524
|
+
contextLines.push(line);
|
|
14525
|
+
if (i < lines.length - 1) contextLines.push(lines[i + 1]);
|
|
14526
|
+
const relativePath = relative(targetDir, file);
|
|
14527
|
+
references.push({
|
|
14528
|
+
target,
|
|
14529
|
+
file: relativePath,
|
|
14530
|
+
line: i + 1,
|
|
14531
|
+
context: contextLines.join("\n")
|
|
14532
|
+
});
|
|
14533
|
+
}
|
|
14534
|
+
}
|
|
14535
|
+
return references;
|
|
14536
|
+
}
|
|
14537
|
+
}
|
|
14538
|
+
/**
|
|
14539
|
+
* Creates and returns the default instruction analysis gateway
|
|
14540
|
+
*/ function createInstructionAnalysisGateway() {
|
|
14541
|
+
return new FileSystemInstructionAnalysisGateway();
|
|
14542
|
+
}
|
|
14543
|
+
|
|
14544
|
+
;// CONCATENATED MODULE: ./src/gateways/script-discovery-gateway.ts
|
|
14545
|
+
/**
|
|
14546
|
+
* Gateway for discovering scripts from package.json manifests
|
|
14547
|
+
*/
|
|
14548
|
+
|
|
14549
|
+
|
|
14550
|
+
|
|
14551
|
+
/**
|
|
14552
|
+
* File system implementation of script discovery gateway
|
|
14553
|
+
*/ class FileSystemScriptDiscoveryGateway {
|
|
14554
|
+
async discoverScripts(targetDir) {
|
|
14555
|
+
const packageJsonPath = join(targetDir, "package.json");
|
|
14556
|
+
if (!await fileExists(packageJsonPath)) {
|
|
14557
|
+
return [];
|
|
14558
|
+
}
|
|
14559
|
+
try {
|
|
14560
|
+
const content = await readFile(packageJsonPath, "utf-8");
|
|
14561
|
+
const packageJson = JSON.parse(content);
|
|
14562
|
+
if (!packageJson.scripts) {
|
|
14563
|
+
return [];
|
|
14564
|
+
}
|
|
14565
|
+
const scripts = [];
|
|
14566
|
+
for (const [name, command] of Object.entries(packageJson.scripts)){
|
|
14567
|
+
const phase = determineScriptPhase(name, command);
|
|
14568
|
+
const isMandatory = isScriptMandatory(phase);
|
|
14569
|
+
scripts.push({
|
|
14570
|
+
name,
|
|
14571
|
+
command,
|
|
14572
|
+
phase,
|
|
14573
|
+
isMandatory
|
|
14574
|
+
});
|
|
14575
|
+
}
|
|
14576
|
+
return scripts;
|
|
14577
|
+
} catch {
|
|
14578
|
+
// If package.json is malformed or cannot be parsed, return empty array
|
|
14579
|
+
return [];
|
|
14580
|
+
}
|
|
14581
|
+
}
|
|
14582
|
+
}
|
|
14583
|
+
/**
|
|
14584
|
+
* Creates and returns the default script discovery gateway
|
|
14585
|
+
*/ function createScriptDiscoveryGateway() {
|
|
14586
|
+
return new FileSystemScriptDiscoveryGateway();
|
|
14587
|
+
}
|
|
14588
|
+
|
|
14421
14589
|
;// CONCATENATED MODULE: ./src/gateways/skill-file-gateway.ts
|
|
14422
14590
|
/**
|
|
14423
14591
|
* Gateway for skill file system operations.
|
|
@@ -14436,7 +14604,7 @@ async function watchConfig(options) {
|
|
|
14436
14604
|
}
|
|
14437
14605
|
async skillDirectoryExists(targetDir, skillName) {
|
|
14438
14606
|
const dirPath = this.getSkillDirectoryPath(targetDir, skillName);
|
|
14439
|
-
return
|
|
14607
|
+
return file_system_utils_fileExists(dirPath);
|
|
14440
14608
|
}
|
|
14441
14609
|
async ensureSkillDirectory(targetDir, skillName) {
|
|
14442
14610
|
const skillDir = this.getSkillDirectoryPath(targetDir, skillName);
|
|
@@ -14459,6 +14627,160 @@ async function watchConfig(options) {
|
|
|
14459
14627
|
|
|
14460
14628
|
// EXTERNAL MODULE: ./node_modules/yaml/dist/index.js
|
|
14461
14629
|
var dist = __webpack_require__(1198);
|
|
14630
|
+
;// CONCATENATED MODULE: ./src/gateways/tool-discovery-gateway.ts
|
|
14631
|
+
/**
|
|
14632
|
+
* Gateway for discovering CLI tools and commands from GitHub Actions workflows
|
|
14633
|
+
*/
|
|
14634
|
+
|
|
14635
|
+
|
|
14636
|
+
|
|
14637
|
+
|
|
14638
|
+
/**
|
|
14639
|
+
* File system implementation of tool discovery gateway
|
|
14640
|
+
*/ class FileSystemToolDiscoveryGateway {
|
|
14641
|
+
async discoverTools(targetDir) {
|
|
14642
|
+
const workflowsDir = join(targetDir, ".github", "workflows");
|
|
14643
|
+
if (!await fileExists(workflowsDir)) {
|
|
14644
|
+
return [];
|
|
14645
|
+
}
|
|
14646
|
+
const files = await readdir(workflowsDir);
|
|
14647
|
+
const yamlFiles = files.filter((f)=>f.endsWith(".yml") || f.endsWith(".yaml"));
|
|
14648
|
+
const allTools = [];
|
|
14649
|
+
for (const file of yamlFiles){
|
|
14650
|
+
const filePath = join(workflowsDir, file);
|
|
14651
|
+
try {
|
|
14652
|
+
const content = await readFile(filePath, "utf-8");
|
|
14653
|
+
const workflow = parseYaml(content);
|
|
14654
|
+
const tools = this.extractToolsFromWorkflow(workflow, file);
|
|
14655
|
+
allTools.push(...tools);
|
|
14656
|
+
} catch {
|
|
14657
|
+
// Skip files that can't be parsed as valid YAML
|
|
14658
|
+
}
|
|
14659
|
+
}
|
|
14660
|
+
return this.deduplicateTools(allTools);
|
|
14661
|
+
}
|
|
14662
|
+
extractToolsFromWorkflow(workflow, sourceFile) {
|
|
14663
|
+
const tools = [];
|
|
14664
|
+
if (!workflow || typeof workflow !== "object") {
|
|
14665
|
+
return tools;
|
|
14666
|
+
}
|
|
14667
|
+
const jobs = workflow.jobs;
|
|
14668
|
+
if (!jobs || typeof jobs !== "object") {
|
|
14669
|
+
return tools;
|
|
14670
|
+
}
|
|
14671
|
+
for (const job of Object.values(jobs)){
|
|
14672
|
+
if (!job || typeof job !== "object") {
|
|
14673
|
+
continue;
|
|
14674
|
+
}
|
|
14675
|
+
const steps = job.steps;
|
|
14676
|
+
if (!Array.isArray(steps)) {
|
|
14677
|
+
continue;
|
|
14678
|
+
}
|
|
14679
|
+
for (const step of steps){
|
|
14680
|
+
if (!step || typeof step !== "object") {
|
|
14681
|
+
continue;
|
|
14682
|
+
}
|
|
14683
|
+
const stepObj = step;
|
|
14684
|
+
const run = stepObj.run;
|
|
14685
|
+
if (typeof run === "string") {
|
|
14686
|
+
// Extract tools from run commands
|
|
14687
|
+
const extractedTools = this.extractToolsFromRunCommand(run, sourceFile);
|
|
14688
|
+
tools.push(...extractedTools);
|
|
14689
|
+
}
|
|
14690
|
+
}
|
|
14691
|
+
}
|
|
14692
|
+
return tools;
|
|
14693
|
+
}
|
|
14694
|
+
extractToolsFromRunCommand(runCommand, sourceFile) {
|
|
14695
|
+
const tools = [];
|
|
14696
|
+
// Split by newlines and pipe tokens with surrounding whitespace to handle
|
|
14697
|
+
// multi-line and piped commands without breaking on constructs like "cmd || true"
|
|
14698
|
+
const commands = runCommand.split(/\n|\s\|\s/).map((c)=>c.trim()).filter((c)=>c.length > 0 && !c.startsWith("#"));
|
|
14699
|
+
for (const command of commands){
|
|
14700
|
+
// Extract the base command (first word)
|
|
14701
|
+
const parts = command.split(/\s+/);
|
|
14702
|
+
const baseCommand = parts[0];
|
|
14703
|
+
// Skip shell built-ins and common utilities
|
|
14704
|
+
if (this.isShellBuiltin(baseCommand)) {
|
|
14705
|
+
continue;
|
|
14706
|
+
}
|
|
14707
|
+
// Determine tool name and full command
|
|
14708
|
+
let toolName;
|
|
14709
|
+
const fullCommand = command;
|
|
14710
|
+
// Handle special cases like "npm run", "mise run", etc.
|
|
14711
|
+
if (parts.length >= 2 && (baseCommand === "npm" || baseCommand === "mise") && parts[1] === "run") {
|
|
14712
|
+
if (parts.length >= 3 && parts[2]) {
|
|
14713
|
+
// "npm run test" -> name: "npm run test"
|
|
14714
|
+
toolName = parts.slice(0, 3).join(" ");
|
|
14715
|
+
} else {
|
|
14716
|
+
// Handle commands like "npm run" without a script name
|
|
14717
|
+
toolName = parts.slice(0, 2).join(" ");
|
|
14718
|
+
}
|
|
14719
|
+
} else if (parts.length >= 2) {
|
|
14720
|
+
// For most tools, include the subcommand for better specificity
|
|
14721
|
+
// e.g., "npm test", "npx biome", "pnpm lint"
|
|
14722
|
+
toolName = parts.slice(0, 2).join(" ");
|
|
14723
|
+
} else {
|
|
14724
|
+
// Fallback to the base command if no subcommand is present
|
|
14725
|
+
toolName = baseCommand;
|
|
14726
|
+
}
|
|
14727
|
+
const phase = this.determineToolPhase(toolName, fullCommand);
|
|
14728
|
+
const isMandatory = isScriptMandatory(phase);
|
|
14729
|
+
tools.push({
|
|
14730
|
+
name: toolName,
|
|
14731
|
+
fullCommand,
|
|
14732
|
+
phase,
|
|
14733
|
+
isMandatory,
|
|
14734
|
+
sourceWorkflow: sourceFile
|
|
14735
|
+
});
|
|
14736
|
+
}
|
|
14737
|
+
return tools;
|
|
14738
|
+
}
|
|
14739
|
+
isShellBuiltin(command) {
|
|
14740
|
+
const builtins = [
|
|
14741
|
+
"cd",
|
|
14742
|
+
"echo",
|
|
14743
|
+
"mkdir",
|
|
14744
|
+
"rm",
|
|
14745
|
+
"cp",
|
|
14746
|
+
"mv",
|
|
14747
|
+
"test",
|
|
14748
|
+
"[",
|
|
14749
|
+
"if",
|
|
14750
|
+
"then",
|
|
14751
|
+
"else",
|
|
14752
|
+
"fi",
|
|
14753
|
+
"for",
|
|
14754
|
+
"while",
|
|
14755
|
+
"do",
|
|
14756
|
+
"done",
|
|
14757
|
+
"case",
|
|
14758
|
+
"esac"
|
|
14759
|
+
];
|
|
14760
|
+
return builtins.includes(command);
|
|
14761
|
+
}
|
|
14762
|
+
determineToolPhase(toolName, fullCommand) {
|
|
14763
|
+
// Use the same logic as script phase determination
|
|
14764
|
+
return determineScriptPhase(toolName, fullCommand);
|
|
14765
|
+
}
|
|
14766
|
+
deduplicateTools(tools) {
|
|
14767
|
+
const seen = new Map();
|
|
14768
|
+
for (const tool of tools){
|
|
14769
|
+
// Use full command as key for deduplication
|
|
14770
|
+
const key = tool.fullCommand;
|
|
14771
|
+
if (!seen.has(key)) {
|
|
14772
|
+
seen.set(key, tool);
|
|
14773
|
+
}
|
|
14774
|
+
}
|
|
14775
|
+
return Array.from(seen.values());
|
|
14776
|
+
}
|
|
14777
|
+
}
|
|
14778
|
+
/**
|
|
14779
|
+
* Creates and returns the default tool discovery gateway
|
|
14780
|
+
*/ function createToolDiscoveryGateway() {
|
|
14781
|
+
return new FileSystemToolDiscoveryGateway();
|
|
14782
|
+
}
|
|
14783
|
+
|
|
14462
14784
|
;// CONCATENATED MODULE: ./src/use-cases/setup-step-discovery.ts
|
|
14463
14785
|
/**
|
|
14464
14786
|
* Use case for discovering setup steps in workflows.
|
|
@@ -14682,7 +15004,7 @@ var dist = __webpack_require__(1198);
|
|
|
14682
15004
|
const workflowsDir = (0,external_node_path_.join)(targetDir, ".github", "workflows");
|
|
14683
15005
|
for (const filename of COPILOT_SETUP_WORKFLOW_FILENAMES){
|
|
14684
15006
|
const workflowPath = (0,external_node_path_.join)(workflowsDir, filename);
|
|
14685
|
-
if (await
|
|
15007
|
+
if (await file_system_utils_fileExists(workflowPath)) {
|
|
14686
15008
|
return workflowPath;
|
|
14687
15009
|
}
|
|
14688
15010
|
}
|
|
@@ -14690,7 +15012,7 @@ var dist = __webpack_require__(1198);
|
|
|
14690
15012
|
}
|
|
14691
15013
|
async parseWorkflowsForSetupActions(targetDir) {
|
|
14692
15014
|
const workflowsDir = (0,external_node_path_.join)(targetDir, ".github", "workflows");
|
|
14693
|
-
if (!await
|
|
15015
|
+
if (!await file_system_utils_fileExists(workflowsDir)) {
|
|
14694
15016
|
return [];
|
|
14695
15017
|
}
|
|
14696
15018
|
const files = await (0,promises_.readdir)(workflowsDir);
|
|
@@ -14757,6 +15079,9 @@ var dist = __webpack_require__(1198);
|
|
|
14757
15079
|
|
|
14758
15080
|
|
|
14759
15081
|
|
|
15082
|
+
|
|
15083
|
+
|
|
15084
|
+
|
|
14760
15085
|
;// CONCATENATED MODULE: ./src/use-cases/candidate-builder.ts
|
|
14761
15086
|
/**
|
|
14762
15087
|
* Use case for building setup step candidates from environment detection.
|
|
@@ -15619,7 +15944,7 @@ const copilotSetupArgs = {};
|
|
|
15619
15944
|
}
|
|
15620
15945
|
// Step 2: Parse existing workflows for setup actions
|
|
15621
15946
|
const workflowsDir = (0,external_node_path_.join)(targetDir, ".github", "workflows");
|
|
15622
|
-
const workflowsDirExists = await
|
|
15947
|
+
const workflowsDirExists = await file_system_utils_fileExists(workflowsDir);
|
|
15623
15948
|
const workflowCandidates = workflowsDirExists ? await workflowGateway.parseWorkflowsForSetupActions(targetDir) : [];
|
|
15624
15949
|
if (workflowCandidates.length > 0) {
|
|
15625
15950
|
const actionNames = workflowCandidates.map((c)=>c.action).join(", ");
|
|
@@ -30511,7 +30836,7 @@ var Eta = class extends Eta$1 {
|
|
|
30511
30836
|
for (const node of structure.nodes){
|
|
30512
30837
|
const fullPath = (0,external_node_path_.join)(targetDir, node.path);
|
|
30513
30838
|
// Skip if already exists to preserve existing files/directories
|
|
30514
|
-
if (await
|
|
30839
|
+
if (await file_system_utils_fileExists(fullPath)) {
|
|
30515
30840
|
consola.debug(`Skipping existing: ${fullPath}`);
|
|
30516
30841
|
continue;
|
|
30517
30842
|
}
|