@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.
@@ -5610,6 +5610,7 @@ exports["default"] = def;
5610
5610
 
5611
5611
  Object.defineProperty(exports, "__esModule", ({ value: true }));
5612
5612
  const code_1 = __webpack_require__(5765);
5613
+ const util_1 = __webpack_require__(4227);
5613
5614
  const codegen_1 = __webpack_require__(9029);
5614
5615
  const error = {
5615
5616
  message: ({ schemaCode }) => (0, codegen_1.str) `must match pattern "${schemaCode}"`,
@@ -5622,11 +5623,19 @@ const def = {
5622
5623
  $data: true,
5623
5624
  error,
5624
5625
  code(cxt) {
5625
- const { data, $data, schema, schemaCode, it } = cxt;
5626
- // TODO regexp should be wrapped in try/catchs
5626
+ const { gen, data, $data, schema, schemaCode, it } = cxt;
5627
5627
  const u = it.opts.unicodeRegExp ? "u" : "";
5628
- const regExp = $data ? (0, codegen_1._) `(new RegExp(${schemaCode}, ${u}))` : (0, code_1.usePattern)(cxt, schema);
5629
- cxt.fail$data((0, codegen_1._) `!${regExp}.test(${data})`);
5628
+ if ($data) {
5629
+ const { regExp } = it.opts.code;
5630
+ const regExpCode = regExp.code === "new RegExp" ? (0, codegen_1._) `new RegExp` : (0, util_1.useFunc)(gen, regExp);
5631
+ const valid = gen.let("valid");
5632
+ gen.try(() => gen.assign(valid, (0, codegen_1._) `${regExpCode}(${schemaCode}, ${u}).test(${data})`), () => gen.assign(valid, false));
5633
+ cxt.fail$data((0, codegen_1._) `!${valid}`);
5634
+ }
5635
+ else {
5636
+ const regExp = (0, code_1.usePattern)(cxt, schema);
5637
+ cxt.fail$data((0, codegen_1._) `!${regExp}.test(${data})`);
5638
+ }
5630
5639
  },
5631
5640
  };
5632
5641
  exports["default"] = def;
@@ -45199,6 +45208,283 @@ async function watchConfig(options) {
45199
45208
  return new FileSystemEnvironmentGateway();
45200
45209
  }
45201
45210
 
45211
+ ;// CONCATENATED MODULE: ./src/gateways/instruction-analysis-gateway.ts
45212
+ /**
45213
+ * Gateway for analyzing repository instructions for feedback loop coverage
45214
+ */
45215
+
45216
+
45217
+ /**
45218
+ * File system implementation of instruction analysis gateway
45219
+ */ class FileSystemInstructionAnalysisGateway {
45220
+ async analyzeCoverage(targetDir, scripts, tools) {
45221
+ // Find all instruction files
45222
+ const instructionFiles = await this.findInstructionFiles(targetDir);
45223
+ // Read and search instruction content
45224
+ const references = [];
45225
+ const documentedTargets = new Set();
45226
+ for (const file of instructionFiles){
45227
+ const content = await (0,promises_.readFile)(file, "utf-8");
45228
+ const lines = content.split("\n");
45229
+ // Check for script references (e.g., "npm test", "npm run build")
45230
+ for (const script of scripts){
45231
+ const scriptRefs = this.findReferencesInContent(script.name, content, lines, file, targetDir);
45232
+ if (scriptRefs.length > 0) {
45233
+ references.push(...scriptRefs);
45234
+ documentedTargets.add(script.name);
45235
+ }
45236
+ }
45237
+ // Check for tool references (e.g., "mise run test", "biome check")
45238
+ for (const tool of tools){
45239
+ const toolRefs = this.findReferencesInContent(tool.name, content, lines, file, targetDir);
45240
+ if (toolRefs.length > 0) {
45241
+ references.push(...toolRefs);
45242
+ documentedTargets.add(tool.name);
45243
+ }
45244
+ }
45245
+ }
45246
+ // Filter mandatory scripts/tools
45247
+ const mandatoryScripts = scripts.filter((s)=>s.isMandatory);
45248
+ const mandatoryTools = tools.filter((t)=>t.isMandatory);
45249
+ const allMandatory = [
45250
+ ...mandatoryScripts,
45251
+ ...mandatoryTools
45252
+ ];
45253
+ // Categorize as missing or documented
45254
+ const missingInInstructions = allMandatory.filter((item)=>!documentedTargets.has(item.name));
45255
+ const documentedInInstructions = allMandatory.filter((item)=>documentedTargets.has(item.name));
45256
+ const totalMandatory = allMandatory.length;
45257
+ const totalDocumented = documentedInInstructions.length;
45258
+ const coveragePercentage = totalMandatory === 0 ? 100 : totalDocumented / totalMandatory * 100;
45259
+ return {
45260
+ missingInInstructions,
45261
+ documentedInInstructions,
45262
+ references,
45263
+ summary: {
45264
+ totalMandatory,
45265
+ totalDocumented,
45266
+ coveragePercentage: Math.round(coveragePercentage * 100) / 100
45267
+ }
45268
+ };
45269
+ }
45270
+ async findInstructionFiles(targetDir) {
45271
+ const files = [];
45272
+ // Check for .github/copilot-instructions.md
45273
+ const copilotInstructions = (0,external_node_path_.join)(targetDir, ".github", "copilot-instructions.md");
45274
+ if (await file_system_utils_fileExists(copilotInstructions)) {
45275
+ files.push(copilotInstructions);
45276
+ }
45277
+ // Check for .github/instructions/*.md
45278
+ const instructionsDir = (0,external_node_path_.join)(targetDir, ".github", "instructions");
45279
+ if (await file_system_utils_fileExists(instructionsDir)) {
45280
+ try {
45281
+ const instructionFiles = await (0,promises_.readdir)(instructionsDir);
45282
+ for (const file of instructionFiles){
45283
+ if (file.endsWith(".md")) {
45284
+ files.push((0,external_node_path_.join)(instructionsDir, file));
45285
+ }
45286
+ }
45287
+ } catch {
45288
+ // Skip directory if we can't read it (e.g., permissions issues)
45289
+ // Similar to how workflow parsing errors are handled
45290
+ }
45291
+ }
45292
+ return files;
45293
+ }
45294
+ findReferencesInContent(target, content, lines, file, targetDir) {
45295
+ const references = [];
45296
+ // Build a case-insensitive, word-boundary-aware pattern for the target.
45297
+ // This reduces false positives from simple substring matches like
45298
+ // "test" in "testing" or "latest", while still matching common
45299
+ // separators such as spaces, punctuation, etc.
45300
+ const escapedTarget = target.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
45301
+ const targetPattern = new RegExp(`(?:^|[^\\w])(${escapedTarget})(?=$|[^\\w])`, "i");
45302
+ // Fast path: skip line-by-line processing if the target pattern
45303
+ // never appears in the full content.
45304
+ if (!targetPattern.test(content)) {
45305
+ return references;
45306
+ }
45307
+ // Find line numbers where target appears
45308
+ for(let i = 0; i < lines.length; i++){
45309
+ const line = lines[i];
45310
+ if (targetPattern.test(line)) {
45311
+ // Get context (line before and after if available)
45312
+ const contextLines = [];
45313
+ if (i > 0) contextLines.push(lines[i - 1]);
45314
+ contextLines.push(line);
45315
+ if (i < lines.length - 1) contextLines.push(lines[i + 1]);
45316
+ const relativePath = (0,external_node_path_.relative)(targetDir, file);
45317
+ references.push({
45318
+ target,
45319
+ file: relativePath,
45320
+ line: i + 1,
45321
+ context: contextLines.join("\n")
45322
+ });
45323
+ }
45324
+ }
45325
+ return references;
45326
+ }
45327
+ }
45328
+ /**
45329
+ * Creates and returns the default instruction analysis gateway
45330
+ */ function createInstructionAnalysisGateway() {
45331
+ return new FileSystemInstructionAnalysisGateway();
45332
+ }
45333
+
45334
+ ;// CONCATENATED MODULE: ./src/entities/feedback-loop.ts
45335
+ /**
45336
+ * Core domain entities for SDLC feedback loop discovery and validation.
45337
+ * These types represent scripts, tools, and their mapping to SDLC phases.
45338
+ */ /**
45339
+ * SDLC feedback loop phases that scripts and tools support
45340
+ */ /**
45341
+ * Maps common script names to SDLC phases
45342
+ */ const SCRIPT_PHASE_MAPPING = {
45343
+ test: "test",
45344
+ "test:unit": "test",
45345
+ "test:integration": "test",
45346
+ "test:e2e": "test",
45347
+ "test:watch": "test",
45348
+ build: "build",
45349
+ compile: "build",
45350
+ bundle: "build",
45351
+ lint: "lint",
45352
+ "lint:fix": "lint",
45353
+ "lint:check": "lint",
45354
+ "lint:workflows": "lint",
45355
+ "lint:yaml": "lint",
45356
+ format: "format",
45357
+ "format:check": "format",
45358
+ "format:fix": "format",
45359
+ prettier: "format",
45360
+ "prettier:check": "format",
45361
+ "prettier:fix": "format",
45362
+ audit: "security",
45363
+ "audit:fix": "security",
45364
+ security: "security",
45365
+ deploy: "deploy",
45366
+ publish: "deploy",
45367
+ release: "deploy",
45368
+ install: "install",
45369
+ ci: "install",
45370
+ dev: "dev",
45371
+ start: "dev",
45372
+ serve: "dev"
45373
+ };
45374
+ /**
45375
+ * Scripts that are considered mandatory for agent feedback loops
45376
+ */ const MANDATORY_SCRIPT_NAMES = (/* unused pure expression or super */ null && ([
45377
+ "test",
45378
+ "build",
45379
+ "lint",
45380
+ "format"
45381
+ ]));
45382
+ /**
45383
+ * Determines the SDLC phase for a script based on its name and command
45384
+ */ function determineScriptPhase(scriptName, command) {
45385
+ // Check exact name match first
45386
+ const exactMatch = SCRIPT_PHASE_MAPPING[scriptName];
45387
+ if (exactMatch) {
45388
+ return exactMatch;
45389
+ }
45390
+ // Check if name starts with a known phase
45391
+ for (const [pattern, phase] of Object.entries(SCRIPT_PHASE_MAPPING)){
45392
+ // Skip patterns we've already handled as exact matches
45393
+ if (pattern === scriptName) {
45394
+ continue;
45395
+ }
45396
+ if (!scriptName.startsWith(pattern)) {
45397
+ continue;
45398
+ }
45399
+ // If the pattern itself includes a separator (e.g. "test:unit"),
45400
+ // allow simple prefix matching (handles "test:unit:watch", etc.)
45401
+ const lastCharOfPattern = pattern.charAt(pattern.length - 1);
45402
+ if (lastCharOfPattern === ":") {
45403
+ return phase;
45404
+ }
45405
+ // For generic patterns like "test" or "build", require that the next
45406
+ // character after the pattern is a colon to avoid matching names
45407
+ // like "test-utils" or "build-tools".
45408
+ const nextChar = scriptName.charAt(pattern.length);
45409
+ if (nextChar === ":") {
45410
+ return phase;
45411
+ }
45412
+ }
45413
+ // Analyze command content for hints (not script name, since we already checked patterns)
45414
+ const lowerCommand = command.toLowerCase();
45415
+ if (lowerCommand.includes("test") || lowerCommand.includes("vitest") || lowerCommand.includes("jest") || lowerCommand.includes("mocha") || lowerCommand.includes("ava")) {
45416
+ return "test";
45417
+ }
45418
+ if (lowerCommand.includes("build") || lowerCommand.includes("compile") || lowerCommand.includes("webpack") || lowerCommand.includes("rspack") || lowerCommand.includes("rollup") || lowerCommand.includes("vite build")) {
45419
+ return "build";
45420
+ }
45421
+ if (lowerCommand.includes("lint") || lowerCommand.includes("eslint") || lowerCommand.includes("biome") || lowerCommand.includes("tslint") || lowerCommand.includes("actionlint") || lowerCommand.includes("yamllint")) {
45422
+ return "lint";
45423
+ }
45424
+ if (lowerCommand.includes("prettier") || lowerCommand.includes("format")) {
45425
+ return "format";
45426
+ }
45427
+ if (lowerCommand.includes("audit") || lowerCommand.includes("snyk") || lowerCommand.includes("npm-audit")) {
45428
+ return "security";
45429
+ }
45430
+ return "unknown";
45431
+ }
45432
+ /**
45433
+ * Determines if a script is mandatory based on its phase
45434
+ */ function isScriptMandatory(phase) {
45435
+ return [
45436
+ "test",
45437
+ "build",
45438
+ "lint",
45439
+ "format"
45440
+ ].includes(phase);
45441
+ }
45442
+
45443
+ ;// CONCATENATED MODULE: ./src/gateways/script-discovery-gateway.ts
45444
+ /**
45445
+ * Gateway for discovering scripts from package.json manifests
45446
+ */
45447
+
45448
+
45449
+
45450
+ /**
45451
+ * File system implementation of script discovery gateway
45452
+ */ class FileSystemScriptDiscoveryGateway {
45453
+ async discoverScripts(targetDir) {
45454
+ const packageJsonPath = (0,external_node_path_.join)(targetDir, "package.json");
45455
+ if (!await file_system_utils_fileExists(packageJsonPath)) {
45456
+ return [];
45457
+ }
45458
+ try {
45459
+ const content = await (0,promises_.readFile)(packageJsonPath, "utf-8");
45460
+ const packageJson = JSON.parse(content);
45461
+ if (!packageJson.scripts) {
45462
+ return [];
45463
+ }
45464
+ const scripts = [];
45465
+ for (const [name, command] of Object.entries(packageJson.scripts)){
45466
+ const phase = determineScriptPhase(name, command);
45467
+ const isMandatory = isScriptMandatory(phase);
45468
+ scripts.push({
45469
+ name,
45470
+ command,
45471
+ phase,
45472
+ isMandatory
45473
+ });
45474
+ }
45475
+ return scripts;
45476
+ } catch {
45477
+ // If package.json is malformed or cannot be parsed, return empty array
45478
+ return [];
45479
+ }
45480
+ }
45481
+ }
45482
+ /**
45483
+ * Creates and returns the default script discovery gateway
45484
+ */ function createScriptDiscoveryGateway() {
45485
+ return new FileSystemScriptDiscoveryGateway();
45486
+ }
45487
+
45202
45488
  ;// CONCATENATED MODULE: ./src/gateways/skill-file-gateway.ts
45203
45489
  /**
45204
45490
  * Gateway for skill file system operations.
@@ -45238,6 +45524,160 @@ async function watchConfig(options) {
45238
45524
  return new FileSystemSkillFileGateway();
45239
45525
  }
45240
45526
 
45527
+ ;// CONCATENATED MODULE: ./src/gateways/tool-discovery-gateway.ts
45528
+ /**
45529
+ * Gateway for discovering CLI tools and commands from GitHub Actions workflows
45530
+ */
45531
+
45532
+
45533
+
45534
+
45535
+ /**
45536
+ * File system implementation of tool discovery gateway
45537
+ */ class FileSystemToolDiscoveryGateway {
45538
+ async discoverTools(targetDir) {
45539
+ const workflowsDir = (0,external_node_path_.join)(targetDir, ".github", "workflows");
45540
+ if (!await file_system_utils_fileExists(workflowsDir)) {
45541
+ return [];
45542
+ }
45543
+ const files = await (0,promises_.readdir)(workflowsDir);
45544
+ const yamlFiles = files.filter((f)=>f.endsWith(".yml") || f.endsWith(".yaml"));
45545
+ const allTools = [];
45546
+ for (const file of yamlFiles){
45547
+ const filePath = (0,external_node_path_.join)(workflowsDir, file);
45548
+ try {
45549
+ const content = await (0,promises_.readFile)(filePath, "utf-8");
45550
+ const workflow = (0,yaml_dist.parse)(content);
45551
+ const tools = this.extractToolsFromWorkflow(workflow, file);
45552
+ allTools.push(...tools);
45553
+ } catch {
45554
+ // Skip files that can't be parsed as valid YAML
45555
+ }
45556
+ }
45557
+ return this.deduplicateTools(allTools);
45558
+ }
45559
+ extractToolsFromWorkflow(workflow, sourceFile) {
45560
+ const tools = [];
45561
+ if (!workflow || typeof workflow !== "object") {
45562
+ return tools;
45563
+ }
45564
+ const jobs = workflow.jobs;
45565
+ if (!jobs || typeof jobs !== "object") {
45566
+ return tools;
45567
+ }
45568
+ for (const job of Object.values(jobs)){
45569
+ if (!job || typeof job !== "object") {
45570
+ continue;
45571
+ }
45572
+ const steps = job.steps;
45573
+ if (!Array.isArray(steps)) {
45574
+ continue;
45575
+ }
45576
+ for (const step of steps){
45577
+ if (!step || typeof step !== "object") {
45578
+ continue;
45579
+ }
45580
+ const stepObj = step;
45581
+ const run = stepObj.run;
45582
+ if (typeof run === "string") {
45583
+ // Extract tools from run commands
45584
+ const extractedTools = this.extractToolsFromRunCommand(run, sourceFile);
45585
+ tools.push(...extractedTools);
45586
+ }
45587
+ }
45588
+ }
45589
+ return tools;
45590
+ }
45591
+ extractToolsFromRunCommand(runCommand, sourceFile) {
45592
+ const tools = [];
45593
+ // Split by newlines and pipe tokens with surrounding whitespace to handle
45594
+ // multi-line and piped commands without breaking on constructs like "cmd || true"
45595
+ const commands = runCommand.split(/\n|\s\|\s/).map((c)=>c.trim()).filter((c)=>c.length > 0 && !c.startsWith("#"));
45596
+ for (const command of commands){
45597
+ // Extract the base command (first word)
45598
+ const parts = command.split(/\s+/);
45599
+ const baseCommand = parts[0];
45600
+ // Skip shell built-ins and common utilities
45601
+ if (this.isShellBuiltin(baseCommand)) {
45602
+ continue;
45603
+ }
45604
+ // Determine tool name and full command
45605
+ let toolName;
45606
+ const fullCommand = command;
45607
+ // Handle special cases like "npm run", "mise run", etc.
45608
+ if (parts.length >= 2 && (baseCommand === "npm" || baseCommand === "mise") && parts[1] === "run") {
45609
+ if (parts.length >= 3 && parts[2]) {
45610
+ // "npm run test" -> name: "npm run test"
45611
+ toolName = parts.slice(0, 3).join(" ");
45612
+ } else {
45613
+ // Handle commands like "npm run" without a script name
45614
+ toolName = parts.slice(0, 2).join(" ");
45615
+ }
45616
+ } else if (parts.length >= 2) {
45617
+ // For most tools, include the subcommand for better specificity
45618
+ // e.g., "npm test", "npx biome", "pnpm lint"
45619
+ toolName = parts.slice(0, 2).join(" ");
45620
+ } else {
45621
+ // Fallback to the base command if no subcommand is present
45622
+ toolName = baseCommand;
45623
+ }
45624
+ const phase = this.determineToolPhase(toolName, fullCommand);
45625
+ const isMandatory = isScriptMandatory(phase);
45626
+ tools.push({
45627
+ name: toolName,
45628
+ fullCommand,
45629
+ phase,
45630
+ isMandatory,
45631
+ sourceWorkflow: sourceFile
45632
+ });
45633
+ }
45634
+ return tools;
45635
+ }
45636
+ isShellBuiltin(command) {
45637
+ const builtins = [
45638
+ "cd",
45639
+ "echo",
45640
+ "mkdir",
45641
+ "rm",
45642
+ "cp",
45643
+ "mv",
45644
+ "test",
45645
+ "[",
45646
+ "if",
45647
+ "then",
45648
+ "else",
45649
+ "fi",
45650
+ "for",
45651
+ "while",
45652
+ "do",
45653
+ "done",
45654
+ "case",
45655
+ "esac"
45656
+ ];
45657
+ return builtins.includes(command);
45658
+ }
45659
+ determineToolPhase(toolName, fullCommand) {
45660
+ // Use the same logic as script phase determination
45661
+ return determineScriptPhase(toolName, fullCommand);
45662
+ }
45663
+ deduplicateTools(tools) {
45664
+ const seen = new Map();
45665
+ for (const tool of tools){
45666
+ // Use full command as key for deduplication
45667
+ const key = tool.fullCommand;
45668
+ if (!seen.has(key)) {
45669
+ seen.set(key, tool);
45670
+ }
45671
+ }
45672
+ return Array.from(seen.values());
45673
+ }
45674
+ }
45675
+ /**
45676
+ * Creates and returns the default tool discovery gateway
45677
+ */ function createToolDiscoveryGateway() {
45678
+ return new FileSystemToolDiscoveryGateway();
45679
+ }
45680
+
45241
45681
  ;// CONCATENATED MODULE: ./src/use-cases/setup-step-discovery.ts
45242
45682
  /**
45243
45683
  * Use case for discovering setup steps in workflows.
@@ -45536,6 +45976,9 @@ async function watchConfig(options) {
45536
45976
 
45537
45977
 
45538
45978
 
45979
+
45980
+
45981
+
45539
45982
  ;// CONCATENATED MODULE: ./src/mcp/tools/types.ts
45540
45983
  /**
45541
45984
  * Shared types and utilities for MCP tool handlers.
@@ -46611,6 +47054,140 @@ function getGlobalWacContext() {
46611
47054
  });
46612
47055
  };
46613
47056
 
47057
+ ;// CONCATENATED MODULE: ./src/use-cases/discover-feedback-loops.ts
47058
+ /**
47059
+ * Use case for discovering and analyzing SDLC feedback loops
47060
+ */ /**
47061
+ * Use case for discovering scripts and tools that form SDLC feedback loops
47062
+ */ class DiscoverFeedbackLoopsUseCase {
47063
+ scriptGateway;
47064
+ toolGateway;
47065
+ packageManagerGateway;
47066
+ constructor(scriptGateway, toolGateway, packageManagerGateway){
47067
+ this.scriptGateway = scriptGateway;
47068
+ this.toolGateway = toolGateway;
47069
+ this.packageManagerGateway = packageManagerGateway;
47070
+ }
47071
+ async execute(input) {
47072
+ if (!input.targetDir) {
47073
+ throw new Error("Target directory is required");
47074
+ }
47075
+ // Discover scripts from package.json
47076
+ const scripts = await this.scriptGateway.discoverScripts(input.targetDir);
47077
+ // Discover tools from GitHub Actions workflows
47078
+ const tools = await this.toolGateway.discoverTools(input.targetDir);
47079
+ // Detect package manager
47080
+ const packageManagers = await this.packageManagerGateway.detectPackageManagers(input.targetDir);
47081
+ const primaryPackageManager = packageManagers.length > 0 ? packageManagers[0].type : undefined;
47082
+ const feedbackLoops = {
47083
+ scripts: this.sortByPhase(scripts),
47084
+ tools: this.sortByPhase(tools),
47085
+ packageManager: primaryPackageManager
47086
+ };
47087
+ return {
47088
+ feedbackLoops
47089
+ };
47090
+ }
47091
+ /**
47092
+ * Sorts scripts or tools by phase priority
47093
+ * Order: test, lint, format, build, security, install, dev, deploy, unknown
47094
+ */ sortByPhase(items) {
47095
+ const phasePriority = {
47096
+ test: 1,
47097
+ lint: 2,
47098
+ format: 3,
47099
+ build: 4,
47100
+ security: 5,
47101
+ install: 6,
47102
+ dev: 7,
47103
+ deploy: 8,
47104
+ unknown: 9
47105
+ };
47106
+ return [
47107
+ ...items
47108
+ ].sort((a, b)=>{
47109
+ const priorityA = phasePriority[a.phase] ?? 10;
47110
+ const priorityB = phasePriority[b.phase] ?? 10;
47111
+ return priorityA - priorityB;
47112
+ });
47113
+ }
47114
+ }
47115
+
47116
+ ;// CONCATENATED MODULE: ./src/mcp/tools/discover-feedback-loops.ts
47117
+ /**
47118
+ * MCP tool handler for discovering scripts and CLI tools.
47119
+ */
47120
+
47121
+
47122
+
47123
+
47124
+ /**
47125
+ * Discovers npm scripts from package.json and CLI tools from GitHub Actions workflows,
47126
+ * mapping them to SDLC feedback loop phases (test, build, lint, format, etc.).
47127
+ */ const discoverFeedbackLoopsHandler = async (args)=>{
47128
+ const dir = args.targetDir || process.cwd();
47129
+ try {
47130
+ // Create gateways
47131
+ const scriptGateway = createScriptDiscoveryGateway();
47132
+ const toolGateway = createToolDiscoveryGateway();
47133
+ const environmentGateway = createEnvironmentGateway();
47134
+ // Adapter for package manager gateway
47135
+ const packageManagerGateway = {
47136
+ async detectPackageManagers (targetDir) {
47137
+ const env = await environmentGateway.detectEnvironment(targetDir);
47138
+ return env.packageManagers;
47139
+ }
47140
+ };
47141
+ // Create use case
47142
+ const useCase = new DiscoverFeedbackLoopsUseCase(scriptGateway, toolGateway, packageManagerGateway);
47143
+ // Execute discovery
47144
+ const result = await useCase.execute({
47145
+ targetDir: dir
47146
+ });
47147
+ const { scripts, tools, packageManager } = result.feedbackLoops;
47148
+ // Group by phase for better readability
47149
+ const scriptsByPhase = scripts.reduce((acc, script)=>{
47150
+ if (!acc[script.phase]) {
47151
+ acc[script.phase] = [];
47152
+ }
47153
+ acc[script.phase].push({
47154
+ name: script.name,
47155
+ command: script.command,
47156
+ mandatory: script.isMandatory
47157
+ });
47158
+ return acc;
47159
+ }, {});
47160
+ const toolsByPhase = tools.reduce((acc, tool)=>{
47161
+ if (!acc[tool.phase]) {
47162
+ acc[tool.phase] = [];
47163
+ }
47164
+ acc[tool.phase].push({
47165
+ name: tool.name,
47166
+ command: tool.fullCommand,
47167
+ mandatory: tool.isMandatory,
47168
+ source: tool.sourceWorkflow
47169
+ });
47170
+ return acc;
47171
+ }, {});
47172
+ const mandatoryScripts = scripts.filter((s)=>s.isMandatory);
47173
+ const mandatoryTools = tools.filter((t)=>t.isMandatory);
47174
+ return successResponse({
47175
+ summary: {
47176
+ totalScripts: scripts.length,
47177
+ totalTools: tools.length,
47178
+ mandatoryScripts: mandatoryScripts.length,
47179
+ mandatoryTools: mandatoryTools.length,
47180
+ packageManager: packageManager || "none detected"
47181
+ },
47182
+ scriptsByPhase,
47183
+ toolsByPhase,
47184
+ message: `Discovered ${scripts.length} package.json script(s) and ${tools.length} CLI tool(s) from workflows`
47185
+ });
47186
+ } catch (error) {
47187
+ return types_errorResponse(`Failed to discover feedback loops: ${error instanceof Error ? error.message : "Unknown error"}`);
47188
+ }
47189
+ };
47190
+
46614
47191
  ;// CONCATENATED MODULE: ./src/mcp/tools/discover-workflow-setup-actions.ts
46615
47192
  /**
46616
47193
  * MCP tool handler for discovering setup actions in existing workflows.
@@ -46730,6 +47307,162 @@ function getGlobalWacContext() {
46730
47307
  });
46731
47308
  };
46732
47309
 
47310
+ ;// CONCATENATED MODULE: ./src/use-cases/validate-instruction-coverage.ts
47311
+ /**
47312
+ * Use case for validating instruction coverage of feedback loops
47313
+ */ /**
47314
+ * Use case for validating that repository instructions cover mandatory feedback loops
47315
+ */ class ValidateInstructionCoverageUseCase {
47316
+ discoverFeedbackLoops;
47317
+ instructionGateway;
47318
+ constructor(discoverFeedbackLoops, instructionGateway){
47319
+ this.discoverFeedbackLoops = discoverFeedbackLoops;
47320
+ this.instructionGateway = instructionGateway;
47321
+ }
47322
+ async execute(input) {
47323
+ if (!input.targetDir) {
47324
+ throw new Error("Target directory is required");
47325
+ }
47326
+ // First, discover all feedback loops
47327
+ const discoveryResult = await this.discoverFeedbackLoops.execute({
47328
+ targetDir: input.targetDir
47329
+ });
47330
+ const { scripts, tools, packageManager } = discoveryResult.feedbackLoops;
47331
+ // Analyze instruction coverage
47332
+ const coverage = await this.instructionGateway.analyzeCoverage(input.targetDir, scripts, tools);
47333
+ // Generate suggestions for missing documentation
47334
+ const suggestions = this.generateSuggestions(coverage, packageManager || "npm");
47335
+ return {
47336
+ coverage,
47337
+ hasFullCoverage: coverage.summary.coveragePercentage === 100,
47338
+ suggestions
47339
+ };
47340
+ }
47341
+ generateSuggestions(coverage, packageManager) {
47342
+ const suggestions = [];
47343
+ if (coverage.missingInInstructions.length === 0) {
47344
+ suggestions.push("✅ All mandatory feedback loops are documented in instructions");
47345
+ return suggestions;
47346
+ }
47347
+ suggestions.push(`⚠️ ${coverage.missingInInstructions.length} mandatory feedback loop(s) are not documented:`);
47348
+ suggestions.push("");
47349
+ // Group by phase
47350
+ const byPhase = new Map();
47351
+ for (const item of coverage.missingInInstructions){
47352
+ const existing = byPhase.get(item.phase) || [];
47353
+ existing.push(item);
47354
+ byPhase.set(item.phase, existing);
47355
+ }
47356
+ // Generate suggestions per phase
47357
+ for (const [phase, items] of byPhase.entries()){
47358
+ suggestions.push(`${phase.toUpperCase()} phase:`);
47359
+ for (const item of items){
47360
+ if ("command" in item) {
47361
+ // It's a script
47362
+ suggestions.push(` - Document "${packageManager} run ${item.name}" (runs: ${item.command})`);
47363
+ } else {
47364
+ // It's a tool
47365
+ suggestions.push(` - Document "${item.fullCommand}"`);
47366
+ }
47367
+ }
47368
+ suggestions.push("");
47369
+ }
47370
+ suggestions.push("Consider adding these to .github/copilot-instructions.md");
47371
+ suggestions.push("or creating dedicated instruction files in .github/instructions/");
47372
+ return suggestions;
47373
+ }
47374
+ }
47375
+
47376
+ ;// CONCATENATED MODULE: ./src/mcp/tools/validate-instruction-coverage.ts
47377
+ /**
47378
+ * MCP tool handler for validating instruction coverage of feedback loops.
47379
+ */
47380
+
47381
+
47382
+
47383
+
47384
+
47385
+
47386
+ /**
47387
+ * Validates that repository instructions (.github/copilot-instructions.md and .github/instructions/*.md)
47388
+ * document all mandatory feedback loop scripts and tools.
47389
+ * Returns coverage percentage and suggestions for missing documentation.
47390
+ */ const validateInstructionCoverageHandler = async (args)=>{
47391
+ const dir = args.targetDir || process.cwd();
47392
+ try {
47393
+ // Create gateways
47394
+ const scriptGateway = createScriptDiscoveryGateway();
47395
+ const toolGateway = createToolDiscoveryGateway();
47396
+ const environmentGateway = createEnvironmentGateway();
47397
+ const instructionGateway = createInstructionAnalysisGateway();
47398
+ // Adapter for package manager gateway
47399
+ const packageManagerGateway = {
47400
+ async detectPackageManagers (targetDir) {
47401
+ const env = await environmentGateway.detectEnvironment(targetDir);
47402
+ return env.packageManagers;
47403
+ }
47404
+ };
47405
+ // Create use cases
47406
+ const discoverUseCase = new DiscoverFeedbackLoopsUseCase(scriptGateway, toolGateway, packageManagerGateway);
47407
+ const validateUseCase = new ValidateInstructionCoverageUseCase(discoverUseCase, instructionGateway);
47408
+ // Execute validation
47409
+ const result = await validateUseCase.execute({
47410
+ targetDir: dir
47411
+ });
47412
+ const { coverage, hasFullCoverage, suggestions } = result;
47413
+ // Format missing items for better readability
47414
+ const missing = coverage.missingInInstructions.map((item)=>{
47415
+ if ("command" in item) {
47416
+ // Script
47417
+ return {
47418
+ type: "script",
47419
+ name: item.name,
47420
+ phase: item.phase,
47421
+ command: item.command
47422
+ };
47423
+ }
47424
+ // Tool
47425
+ return {
47426
+ type: "tool",
47427
+ name: item.name,
47428
+ phase: item.phase,
47429
+ command: item.fullCommand
47430
+ };
47431
+ });
47432
+ // Format documented items
47433
+ const documented = coverage.documentedInInstructions.map((item)=>{
47434
+ if ("command" in item) {
47435
+ // Script
47436
+ return {
47437
+ type: "script",
47438
+ name: item.name,
47439
+ phase: item.phase
47440
+ };
47441
+ }
47442
+ // Tool
47443
+ return {
47444
+ type: "tool",
47445
+ name: item.name,
47446
+ phase: item.phase
47447
+ };
47448
+ });
47449
+ return successResponse({
47450
+ hasFullCoverage,
47451
+ summary: {
47452
+ totalMandatory: coverage.summary.totalMandatory,
47453
+ totalDocumented: coverage.summary.totalDocumented,
47454
+ coveragePercentage: coverage.summary.coveragePercentage
47455
+ },
47456
+ missing,
47457
+ documented,
47458
+ suggestions,
47459
+ message: hasFullCoverage ? "✅ All mandatory feedback loops are documented" : `⚠️ ${coverage.summary.coveragePercentage.toFixed(1)}% coverage - ${missing.length} mandatory items not documented`
47460
+ });
47461
+ } catch (error) {
47462
+ return types_errorResponse(`Failed to validate instruction coverage: ${error instanceof Error ? error.message : "Unknown error"}`);
47463
+ }
47464
+ };
47465
+
46733
47466
  ;// CONCATENATED MODULE: ./src/mcp/tools/index.ts
46734
47467
  /**
46735
47468
  * MCP tool handlers module exports.
@@ -46741,6 +47474,8 @@ function getGlobalWacContext() {
46741
47474
 
46742
47475
 
46743
47476
 
47477
+
47478
+
46744
47479
  ;// CONCATENATED MODULE: ./src/mcp/server.ts
46745
47480
  /**
46746
47481
  * MCP Server for Copilot Setup Steps workflow management.
@@ -46792,6 +47527,18 @@ function getGlobalWacContext() {
46792
47527
  handler: discoverWorkflowSetupActionsHandler,
46793
47528
  inputSchema: targetDirInputSchema
46794
47529
  },
47530
+ {
47531
+ name: "discover_feedback_loops",
47532
+ 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.)",
47533
+ handler: discoverFeedbackLoopsHandler,
47534
+ inputSchema: targetDirInputSchema
47535
+ },
47536
+ {
47537
+ name: "validate_instruction_coverage",
47538
+ 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.",
47539
+ handler: validateInstructionCoverageHandler,
47540
+ inputSchema: targetDirInputSchema
47541
+ },
46795
47542
  {
46796
47543
  name: "read_copilot_setup_workflow",
46797
47544
  description: "Read the existing Copilot Setup Steps workflow (copilot-setup-steps.yml or .yaml) from a target directory",