@lousy-agents/cli 4.0.2 → 5.1.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 ADDED
@@ -0,0 +1,38 @@
1
+ # @lousy-agents/cli
2
+
3
+ CLI scaffolding for Lousy Agents.
4
+
5
+ Use this package to bootstrap new projects with testing, linting, AI assistant instructions, and GitHub Copilot setup.
6
+
7
+ ## Quick Start
8
+
9
+ ```bash
10
+ npx @lousy-agents/cli init
11
+ ```
12
+
13
+ Common follow-up commands:
14
+
15
+ ```bash
16
+ # Generate Copilot setup workflow in an existing repository
17
+ npx @lousy-agents/cli copilot-setup
18
+
19
+ # Create new resources such as custom agents
20
+ npx @lousy-agents/cli new
21
+
22
+ # Validate skills, agents, and instruction files
23
+ npx @lousy-agents/cli lint
24
+ ```
25
+
26
+ ## Documentation
27
+
28
+ - Project overview: [README](https://github.com/zpratt/lousy-agents#readme)
29
+ - `init` command: [`docs/init.md`](https://github.com/zpratt/lousy-agents/blob/main/docs/init.md)
30
+ - `new` command: [`docs/new.md`](https://github.com/zpratt/lousy-agents/blob/main/docs/new.md)
31
+ - `lint` command: [`docs/lint.md`](https://github.com/zpratt/lousy-agents/blob/main/docs/lint.md)
32
+ - `copilot-setup` command: [`docs/copilot-setup.md`](https://github.com/zpratt/lousy-agents/blob/main/docs/copilot-setup.md)
33
+
34
+ ## Reference Examples
35
+
36
+ - React webapp scaffold: [`packages/cli/ui/copilot-with-react`](https://github.com/zpratt/lousy-agents/tree/main/packages/cli/ui/copilot-with-react)
37
+ - Fastify API scaffold: [`packages/cli/api/copilot-with-fastify`](https://github.com/zpratt/lousy-agents/tree/main/packages/cli/api/copilot-with-fastify)
38
+ - Citty CLI scaffold: [`packages/cli/cli/copilot-with-citty`](https://github.com/zpratt/lousy-agents/tree/main/packages/cli/cli/copilot-with-citty)
@@ -13,7 +13,7 @@
13
13
  "lousy-agents": {
14
14
  "type": "stdio",
15
15
  "command": "npx",
16
- "args": ["-y", "-p", "@lousy-agents/cli", "lousy-agents-mcp"]
16
+ "args": ["-y", "-p", "@lousy-agents/mcp", "lousy-agents-mcp"]
17
17
  }
18
18
  }
19
19
  }
@@ -13,7 +13,7 @@
13
13
  "lousy-agents": {
14
14
  "type": "stdio",
15
15
  "command": "npx",
16
- "args": ["-y", "-p", "@lousy-agents/cli", "lousy-agents-mcp"]
16
+ "args": ["-y", "-p", "@lousy-agents/mcp", "lousy-agents-mcp"]
17
17
  }
18
18
  }
19
19
  }
package/dist/index.js CHANGED
@@ -31542,6 +31542,34 @@ function defaultExec(command, args, options) {
31542
31542
  return new FileSystemInstructionAnalysisGateway();
31543
31543
  }
31544
31544
 
31545
+ ;// CONCATENATED MODULE: ../core/src/gateways/npmrc-gateway.ts
31546
+ /**
31547
+ * Gateway for reading and writing `.npmrc` configuration files.
31548
+ */
31549
+
31550
+ const MAX_NPMRC_BYTES = 64 * 1024;
31551
+ /**
31552
+ * File system implementation of the NpmrcGateway.
31553
+ */ class FileSystemNpmrcGateway {
31554
+ async readNpmrc(targetDir) {
31555
+ const npmrcPath = await file_system_utils_resolveSafePath(targetDir, ".npmrc");
31556
+ if (!await file_system_utils_fileExists(npmrcPath)) {
31557
+ return null;
31558
+ }
31559
+ await assertFileSizeWithinLimit(npmrcPath, MAX_NPMRC_BYTES, "`.npmrc` file");
31560
+ return (0,promises_.readFile)(npmrcPath, "utf-8");
31561
+ }
31562
+ async writeNpmrc(targetDir, content) {
31563
+ const npmrcPath = await file_system_utils_resolveSafePath(targetDir, ".npmrc");
31564
+ await (0,promises_.writeFile)(npmrcPath, content, "utf-8");
31565
+ }
31566
+ }
31567
+ /**
31568
+ * Creates and returns the default NpmrcGateway.
31569
+ */ function createNpmrcGateway() {
31570
+ return new FileSystemNpmrcGateway();
31571
+ }
31572
+
31545
31573
  ;// CONCATENATED MODULE: ../core/src/entities/feedback-loop.ts
31546
31574
  /**
31547
31575
  * Core domain entities for SDLC feedback loop discovery and validation.
@@ -32281,6 +32309,53 @@ function createWorkflowGateway() {
32281
32309
 
32282
32310
 
32283
32311
 
32312
+
32313
+ ;// CONCATENATED MODULE: ../core/src/use-cases/add-agent-shell.ts
32314
+ /**
32315
+ * Use case for adding agent-shell to an npm project's `.npmrc` configuration.
32316
+ * Enables npm script observability via the agent-shell script-shell shim.
32317
+ */ /**
32318
+ * The script-shell entry added to `.npmrc` to enable agent-shell.
32319
+ * Uses a PATH-resolved binary name so it works independently of local node_modules.
32320
+ */ const AGENT_SHELL_NPMRC_ENTRY = "script-shell=agent-shell";
32321
+ /**
32322
+ * Checks whether `.npmrc` content already has an active (non-comment) script-shell entry.
32323
+ * Lines starting with `#` or `;` are treated as comments and ignored.
32324
+ */ function hasScriptShellEntry(content) {
32325
+ return /^\s*script-shell\s*=/m.test(content);
32326
+ }
32327
+ /**
32328
+ * Adds agent-shell to the project's `.npmrc` if not already configured.
32329
+ * Only operates on npm projects.
32330
+ */ async function addAgentShell(input, npmrcGateway) {
32331
+ if (input.packageManager.type !== "npm") {
32332
+ return {
32333
+ wasAdded: false,
32334
+ alreadyConfigured: false
32335
+ };
32336
+ }
32337
+ const existingContent = await npmrcGateway.readNpmrc(input.targetDir);
32338
+ if (existingContent !== null && hasScriptShellEntry(existingContent)) {
32339
+ return {
32340
+ wasAdded: false,
32341
+ alreadyConfigured: true
32342
+ };
32343
+ }
32344
+ const newEntry = `${AGENT_SHELL_NPMRC_ENTRY}\n`;
32345
+ let updatedContent;
32346
+ if (existingContent !== null) {
32347
+ const separator = existingContent.length > 0 && !existingContent.endsWith("\n") ? "\n" : "";
32348
+ updatedContent = `${existingContent}${separator}${newEntry}`;
32349
+ } else {
32350
+ updatedContent = newEntry;
32351
+ }
32352
+ await npmrcGateway.writeNpmrc(input.targetDir, updatedContent);
32353
+ return {
32354
+ wasAdded: true,
32355
+ alreadyConfigured: false
32356
+ };
32357
+ }
32358
+
32284
32359
  ;// CONCATENATED MODULE: ../core/src/use-cases/check-copilot-review-ruleset.ts
32285
32360
  function isCopilotCodeReviewRule(rule) {
32286
32361
  return rule.type === "copilot_code_review";
@@ -34759,6 +34834,7 @@ const consola = dist_createConsola();
34759
34834
 
34760
34835
 
34761
34836
 
34837
+
34762
34838
  const copilotSetupArgs = {};
34763
34839
  const copilotSetupCommand = defineCommand({
34764
34840
  meta: {
@@ -34772,6 +34848,7 @@ const copilotSetupCommand = defineCommand({
34772
34848
  const workflowGateway = createWorkflowGateway();
34773
34849
  const copilotSetupConfig = await loadCopilotSetupConfig();
34774
34850
  const rulesetGateway = context.data?.rulesetGateway ?? await createGitHubRulesetGateway();
34851
+ const npmrcGateway = context.data?.npmrcGateway ?? createNpmrcGateway();
34775
34852
  const prompt = context.data?.prompt ?? ((message, options)=>consola.prompt(message, options));
34776
34853
  consola.info("Detecting environment configuration...");
34777
34854
  // Step 1: Detect environment configuration files
@@ -34814,7 +34891,7 @@ const copilotSetupCommand = defineCommand({
34814
34891
  const missingCandidates = findMissingCandidates(allCandidates, existingActions);
34815
34892
  if (missingCandidates.length === 0) {
34816
34893
  consola.success("Copilot Setup Steps workflow already contains all detected setup steps. No changes needed.");
34817
- await checkAndPromptRuleset(rulesetGateway, targetDir, prompt);
34894
+ await runPostWorkflowSteps(rulesetGateway, npmrcGateway, targetDir, prompt, environment);
34818
34895
  return;
34819
34896
  }
34820
34897
  const missingNames = missingCandidates.map((c)=>c.action).join(", ");
@@ -34834,8 +34911,8 @@ const copilotSetupCommand = defineCommand({
34834
34911
  consola.info(`Included setup steps: ${actionNames}`);
34835
34912
  }
34836
34913
  }
34837
- // Step 6: Check for Copilot PR review rulesets
34838
- await checkAndPromptRuleset(rulesetGateway, targetDir, prompt);
34914
+ // Step 6: Run post-workflow steps (ruleset + agent-shell)
34915
+ await runPostWorkflowSteps(rulesetGateway, npmrcGateway, targetDir, prompt, environment);
34839
34916
  }
34840
34917
  });
34841
34918
  async function checkAndPromptRuleset(rulesetGateway, targetDir, prompt) {
@@ -34878,6 +34955,33 @@ async function checkAndPromptRuleset(rulesetGateway, targetDir, prompt) {
34878
34955
  consola.error(`Failed to create ruleset: ${message}. You may need admin access to the repository.`);
34879
34956
  }
34880
34957
  }
34958
+ async function runPostWorkflowSteps(rulesetGateway, npmrcGateway, targetDir, prompt, environment) {
34959
+ await checkAndPromptRuleset(rulesetGateway, targetDir, prompt);
34960
+ await checkAndPromptAgentShell(npmrcGateway, targetDir, prompt, environment);
34961
+ }
34962
+ async function checkAndPromptAgentShell(npmrcGateway, targetDir, prompt, environment) {
34963
+ const npmPackageManager = environment.packageManagers.find((pm)=>pm.type === "npm");
34964
+ if (!npmPackageManager) {
34965
+ return;
34966
+ }
34967
+ const shouldAdd = await prompt("Would you like to add agent-shell to observe npm script execution?", {
34968
+ type: "confirm",
34969
+ default: true
34970
+ });
34971
+ if (!shouldAdd) {
34972
+ consola.info("Skipping agent-shell setup.");
34973
+ return;
34974
+ }
34975
+ const result = await addAgentShell({
34976
+ targetDir,
34977
+ packageManager: npmPackageManager
34978
+ }, npmrcGateway);
34979
+ if (result.alreadyConfigured) {
34980
+ consola.success("agent-shell is already configured in .npmrc.");
34981
+ return;
34982
+ }
34983
+ consola.success("Added agent-shell to .npmrc. Run `npm install -g @lousy-agents/agent-shell` to complete setup.");
34984
+ }
34881
34985
 
34882
34986
  ;// CONCATENATED MODULE: ./src/lib/config.ts
34883
34987
 
@@ -55810,6 +55914,84 @@ const remark = unified().use(remarkParse).use(remarkStringify).freeze()
55810
55914
  }
55811
55915
  }
55812
55916
 
55917
+ ;// CONCATENATED MODULE: ../core/src/use-cases/apply-severity-filter.ts
55918
+ /**
55919
+ * Shared severity filtering for lint outputs.
55920
+ * Applies rule severity configuration to diagnostics, filtering out "off" rules,
55921
+ * remapping "warn" → "warning", and recalculating summary counts.
55922
+ */ /** Maps a lint target to its config key */ const TARGET_TO_CONFIG_KEY = {
55923
+ skill: "skills",
55924
+ agent: "agents",
55925
+ instruction: "instructions"
55926
+ };
55927
+ /**
55928
+ * Maps config severity to diagnostic severity.
55929
+ * "warn" → "warning", "error" → "error", "off" → null (drop).
55930
+ */ function apply_severity_filter_mapSeverity(configSeverity) {
55931
+ if (configSeverity === "off") {
55932
+ return null;
55933
+ }
55934
+ if (configSeverity === "warn") {
55935
+ return "warning";
55936
+ }
55937
+ return configSeverity;
55938
+ }
55939
+ /**
55940
+ * Filters instruction suggestions based on rule severity configuration.
55941
+ * Drops suggestions whose corresponding rule is "off".
55942
+ * Suggestions without a ruleId pass through unchanged.
55943
+ */ function filterInstructionSuggestions(suggestions, rules) {
55944
+ return suggestions.filter((suggestion)=>{
55945
+ if (!suggestion.ruleId) {
55946
+ return true;
55947
+ }
55948
+ return rules[suggestion.ruleId] !== "off";
55949
+ });
55950
+ }
55951
+ /**
55952
+ * Applies severity filtering to a LintOutput based on rule configuration.
55953
+ * Drops diagnostics for "off" rules, remaps severity for "warn"/"error" rules.
55954
+ * Diagnostics without a ruleId pass through unchanged.
55955
+ * For instruction targets, also filters qualityResult.suggestions.
55956
+ */ function applySeverityFilter(output, rulesConfig) {
55957
+ const configKey = TARGET_TO_CONFIG_KEY[output.target];
55958
+ const targetRules = rulesConfig[configKey];
55959
+ const filteredDiagnostics = [];
55960
+ for (const diagnostic of output.diagnostics){
55961
+ const configuredSeverity = diagnostic.ruleId ? targetRules[diagnostic.ruleId] : undefined;
55962
+ if (!configuredSeverity) {
55963
+ filteredDiagnostics.push(diagnostic);
55964
+ continue;
55965
+ }
55966
+ const mappedSeverity = apply_severity_filter_mapSeverity(configuredSeverity);
55967
+ if (mappedSeverity === null) {
55968
+ continue;
55969
+ }
55970
+ filteredDiagnostics.push({
55971
+ ...diagnostic,
55972
+ severity: mappedSeverity
55973
+ });
55974
+ }
55975
+ const totalErrors = filteredDiagnostics.filter((d)=>d.severity === "error").length;
55976
+ const totalWarnings = filteredDiagnostics.filter((d)=>d.severity === "warning").length;
55977
+ const totalInfos = filteredDiagnostics.filter((d)=>d.severity === "info").length;
55978
+ const filteredQualityResult = output.qualityResult && configKey === "instructions" ? {
55979
+ ...output.qualityResult,
55980
+ suggestions: filterInstructionSuggestions(output.qualityResult.suggestions, targetRules)
55981
+ } : output.qualityResult;
55982
+ return {
55983
+ ...output,
55984
+ diagnostics: filteredDiagnostics,
55985
+ qualityResult: filteredQualityResult,
55986
+ summary: {
55987
+ ...output.summary,
55988
+ totalErrors,
55989
+ totalWarnings,
55990
+ totalInfos
55991
+ }
55992
+ };
55993
+ }
55994
+
55813
55995
  ;// CONCATENATED MODULE: ../core/src/use-cases/lint-agent-frontmatter.ts
55814
55996
  /**
55815
55997
  * Use case for linting GitHub Copilot custom agent frontmatter.
@@ -56111,6 +56293,7 @@ function hasFrontmatterDelimiters(content) {
56111
56293
 
56112
56294
 
56113
56295
 
56296
+
56114
56297
  /** Schema for validating target directory */ const TargetDirSchema = schemas_string().min(1, "Target directory is required");
56115
56298
  /**
56116
56299
  * Validates the target directory.
@@ -56275,78 +56458,6 @@ function hasFrontmatterDelimiters(content) {
56275
56458
  consola.warn(suggestion.message);
56276
56459
  }
56277
56460
  }
56278
- /** Maps a lint target to its config key */ const TARGET_TO_CONFIG_KEY = {
56279
- skill: "skills",
56280
- agent: "agents",
56281
- instruction: "instructions"
56282
- };
56283
- /**
56284
- * Maps config-facing severity to diagnostic-facing severity.
56285
- * "warn" → "warning", "error" → "error", "off" → null (drop).
56286
- */ function lint_mapSeverity(configSeverity) {
56287
- if (configSeverity === "off") {
56288
- return null;
56289
- }
56290
- if (configSeverity === "warn") {
56291
- return "warning";
56292
- }
56293
- return configSeverity;
56294
- }
56295
- /**
56296
- * Filters instruction suggestions based on rule severity configuration.
56297
- * Drops suggestions whose corresponding rule is "off".
56298
- * Suggestions without a ruleId pass through unchanged.
56299
- */ function filterInstructionSuggestions(suggestions, rules) {
56300
- return suggestions.filter((suggestion)=>{
56301
- if (!suggestion.ruleId) {
56302
- return true;
56303
- }
56304
- return rules[suggestion.ruleId] !== "off";
56305
- });
56306
- }
56307
- /**
56308
- * Applies severity filtering to a LintOutput based on rule configuration.
56309
- * Drops diagnostics for "off" rules, remaps severity for "warn"/"error" rules.
56310
- * Diagnostics without a ruleId pass through unchanged.
56311
- * For instruction targets, also filters qualityResult.suggestions.
56312
- */ function applySeverityFilter(output, rulesConfig) {
56313
- const configKey = TARGET_TO_CONFIG_KEY[output.target];
56314
- const targetRules = rulesConfig[configKey];
56315
- const filteredDiagnostics = [];
56316
- for (const diagnostic of output.diagnostics){
56317
- const configuredSeverity = diagnostic.ruleId ? targetRules[diagnostic.ruleId] : undefined;
56318
- if (!configuredSeverity) {
56319
- filteredDiagnostics.push(diagnostic);
56320
- continue;
56321
- }
56322
- const mappedSeverity = lint_mapSeverity(configuredSeverity);
56323
- if (mappedSeverity === null) {
56324
- continue;
56325
- }
56326
- filteredDiagnostics.push({
56327
- ...diagnostic,
56328
- severity: mappedSeverity
56329
- });
56330
- }
56331
- const totalErrors = filteredDiagnostics.filter((d)=>d.severity === "error").length;
56332
- const totalWarnings = filteredDiagnostics.filter((d)=>d.severity === "warning").length;
56333
- const totalInfos = filteredDiagnostics.filter((d)=>d.severity === "info").length;
56334
- const filteredQualityResult = output.qualityResult && configKey === "instructions" ? {
56335
- ...output.qualityResult,
56336
- suggestions: filterInstructionSuggestions(output.qualityResult.suggestions, targetRules)
56337
- } : output.qualityResult;
56338
- return {
56339
- ...output,
56340
- diagnostics: filteredDiagnostics,
56341
- qualityResult: filteredQualityResult,
56342
- summary: {
56343
- ...output.summary,
56344
- totalErrors,
56345
- totalWarnings,
56346
- totalInfos
56347
- }
56348
- };
56349
- }
56350
56461
  /**
56351
56462
  * The `lint` command for validating agent skills, custom agents, and instruction files.
56352
56463
  */ const lintCommand = defineCommand({