@lousy-agents/cli 2.2.0 → 2.3.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.
Files changed (28) hide show
  1. package/api/copilot-with-fastify/biome.json +1 -1
  2. package/cli/copilot-with-citty/.devcontainer/devcontainer.json +76 -0
  3. package/cli/copilot-with-citty/.editorconfig +16 -0
  4. package/cli/copilot-with-citty/.github/ISSUE_TEMPLATE/feature-to-spec.yml +54 -0
  5. package/cli/copilot-with-citty/.github/copilot-instructions.md +228 -0
  6. package/cli/copilot-with-citty/.github/instructions/pipeline.instructions.md +92 -0
  7. package/cli/copilot-with-citty/.github/instructions/software-architecture.instructions.md +166 -0
  8. package/cli/copilot-with-citty/.github/instructions/spec.instructions.md +127 -0
  9. package/cli/copilot-with-citty/.github/instructions/test.instructions.md +157 -0
  10. package/cli/copilot-with-citty/.github/specs/README.md +84 -0
  11. package/cli/copilot-with-citty/.github/workflows/assign-copilot.yml +59 -0
  12. package/cli/copilot-with-citty/.github/workflows/ci.yml +67 -0
  13. package/cli/copilot-with-citty/.nvmrc +1 -0
  14. package/cli/copilot-with-citty/.vscode/extensions.json +13 -0
  15. package/cli/copilot-with-citty/.vscode/launch.json +25 -0
  16. package/cli/copilot-with-citty/.vscode/mcp.json +19 -0
  17. package/cli/copilot-with-citty/.yamllint +18 -0
  18. package/cli/copilot-with-citty/biome.json +31 -0
  19. package/cli/copilot-with-citty/package.json +29 -0
  20. package/cli/copilot-with-citty/tsconfig.json +28 -0
  21. package/cli/copilot-with-citty/vitest.config.ts +15 -0
  22. package/cli/copilot-with-citty/vitest.setup.ts +2 -0
  23. package/dist/index.js +224 -59
  24. package/dist/index.js.map +1 -1
  25. package/dist/mcp-server.js +32 -1
  26. package/dist/mcp-server.js.map +1 -1
  27. package/package.json +10 -9
  28. package/ui/copilot-with-react/biome.json +1 -1
package/dist/index.js CHANGED
@@ -14636,6 +14636,15 @@ var dist = __webpack_require__(1198);
14636
14636
  * })
14637
14637
  * workflow.addJob(testJob)
14638
14638
  * ```
14639
+ *
14640
+ * @example
14641
+ * ```typescript
14642
+ * // With custom output path
14643
+ * const workflow = new Workflow('deploy', {
14644
+ * name: 'Deploy',
14645
+ * on: { push: { branches: ['main'] } },
14646
+ * }, { outputPath: 'packages/app-a/.github/workflows' })
14647
+ * ```
14639
14648
  */
14640
14649
  class Workflow {
14641
14650
  workflow;
@@ -14643,6 +14652,11 @@ class Workflow {
14643
14652
  * The filename of the workflow e.g. `main.yml`
14644
14653
  */
14645
14654
  filename;
14655
+ /**
14656
+ * Custom output path for this workflow.
14657
+ * If set, overrides any config file settings.
14658
+ */
14659
+ outputPath;
14646
14660
  addEnvs(envs) {
14647
14661
  if (this.workflow.env && typeof this.workflow.env === 'object')
14648
14662
  this.workflow.env = {
@@ -14667,11 +14681,12 @@ class Workflow {
14667
14681
  };
14668
14682
  return this;
14669
14683
  }
14670
- constructor(filename, workflowProps) {
14684
+ constructor(filename, workflowProps, options) {
14671
14685
  this.filename = filename;
14672
14686
  this.workflow = {
14673
14687
  ...workflowProps,
14674
14688
  };
14689
+ this.outputPath = options?.outputPath;
14675
14690
  }
14676
14691
  }
14677
14692
  //# sourceMappingURL=index.js.map
@@ -15229,6 +15244,7 @@ Example resolved format: actions/setup-node@1a2b3c4d5e6f # v4.0.0`;
15229
15244
  const workflow = new Workflow("copilot-setup-steps.yml", {
15230
15245
  name: "Copilot Setup Steps",
15231
15246
  on: {
15247
+ // biome-ignore lint/style/useNamingConvention: GitHub Actions YAML schema requires snake_case
15232
15248
  workflow_dispatch: {},
15233
15249
  push: {
15234
15250
  branches: [
@@ -15238,6 +15254,7 @@ Example resolved format: actions/setup-node@1a2b3c4d5e6f # v4.0.0`;
15238
15254
  ".github/workflows/copilot-setup-steps.yml"
15239
15255
  ]
15240
15256
  },
15257
+ // biome-ignore lint/style/useNamingConvention: GitHub Actions YAML schema requires snake_case
15241
15258
  pull_request: {
15242
15259
  branches: [
15243
15260
  "main"
@@ -29115,21 +29132,168 @@ const config_dirname = (0,external_node_path_.dirname)(config_filename);
29115
29132
  const PROJECT_ROOT = (0,external_node_path_.join)(config_dirname, "..", "..");
29116
29133
  const WEBAPP_TEMPLATE_DIR = (0,external_node_path_.join)(PROJECT_ROOT, "ui", "copilot-with-react");
29117
29134
  const RESTAPI_TEMPLATE_DIR = (0,external_node_path_.join)(PROJECT_ROOT, "api", "copilot-with-fastify");
29135
+ const CLI_TEMPLATE_DIR = (0,external_node_path_.join)(PROJECT_ROOT, "cli", "copilot-with-citty");
29118
29136
  /**
29119
- * Default CLI project filesystem structure
29120
- */ const DEFAULT_CLI_STRUCTURE = {
29121
- nodes: [
29122
- {
29123
- type: "directory",
29124
- path: ".github/instructions"
29125
- },
29126
- {
29127
- type: "file",
29128
- path: ".github/copilot-instructions.md",
29129
- content: ""
29130
- }
29131
- ]
29132
- };
29137
+ * Helper function to read CLI template files
29138
+ */ function readCliTemplateFile(relativePath) {
29139
+ return readTemplateFile(relativePath, CLI_TEMPLATE_DIR);
29140
+ }
29141
+ /**
29142
+ * Cached CLI structure - lazy-loaded on first access
29143
+ */ let cachedCliStructure = null;
29144
+ /**
29145
+ * Builds the CLI project filesystem structure by reading template files
29146
+ * This is called lazily only when CLI scaffolding is needed
29147
+ */ function buildCliStructure() {
29148
+ if (cachedCliStructure) {
29149
+ return cachedCliStructure;
29150
+ }
29151
+ cachedCliStructure = {
29152
+ nodes: [
29153
+ // Root configuration files
29154
+ {
29155
+ type: "file",
29156
+ path: "package.json",
29157
+ content: readCliTemplateFile("package.json")
29158
+ },
29159
+ {
29160
+ type: "file",
29161
+ path: "tsconfig.json",
29162
+ content: readCliTemplateFile("tsconfig.json")
29163
+ },
29164
+ {
29165
+ type: "file",
29166
+ path: "vitest.config.ts",
29167
+ content: readCliTemplateFile("vitest.config.ts")
29168
+ },
29169
+ {
29170
+ type: "file",
29171
+ path: "vitest.setup.ts",
29172
+ content: readCliTemplateFile("vitest.setup.ts")
29173
+ },
29174
+ {
29175
+ type: "file",
29176
+ path: "biome.json",
29177
+ content: readCliTemplateFile("biome.json")
29178
+ },
29179
+ {
29180
+ type: "file",
29181
+ path: ".editorconfig",
29182
+ content: readCliTemplateFile(".editorconfig")
29183
+ },
29184
+ {
29185
+ type: "file",
29186
+ path: ".nvmrc",
29187
+ content: readCliTemplateFile(".nvmrc")
29188
+ },
29189
+ {
29190
+ type: "file",
29191
+ path: ".yamllint",
29192
+ content: readCliTemplateFile(".yamllint")
29193
+ },
29194
+ // GitHub copilot instructions
29195
+ {
29196
+ type: "directory",
29197
+ path: ".github"
29198
+ },
29199
+ {
29200
+ type: "directory",
29201
+ path: ".github/instructions"
29202
+ },
29203
+ {
29204
+ type: "file",
29205
+ path: ".github/copilot-instructions.md",
29206
+ content: readCliTemplateFile(".github/copilot-instructions.md")
29207
+ },
29208
+ {
29209
+ type: "file",
29210
+ path: ".github/instructions/test.instructions.md",
29211
+ content: readCliTemplateFile(".github/instructions/test.instructions.md")
29212
+ },
29213
+ {
29214
+ type: "file",
29215
+ path: ".github/instructions/spec.instructions.md",
29216
+ content: readCliTemplateFile(".github/instructions/spec.instructions.md")
29217
+ },
29218
+ {
29219
+ type: "file",
29220
+ path: ".github/instructions/pipeline.instructions.md",
29221
+ content: readCliTemplateFile(".github/instructions/pipeline.instructions.md")
29222
+ },
29223
+ {
29224
+ type: "file",
29225
+ path: ".github/instructions/software-architecture.instructions.md",
29226
+ content: readCliTemplateFile(".github/instructions/software-architecture.instructions.md")
29227
+ },
29228
+ // GitHub Issue Templates
29229
+ {
29230
+ type: "directory",
29231
+ path: ".github/ISSUE_TEMPLATE"
29232
+ },
29233
+ {
29234
+ type: "file",
29235
+ path: ".github/ISSUE_TEMPLATE/feature-to-spec.yml",
29236
+ content: readCliTemplateFile(".github/ISSUE_TEMPLATE/feature-to-spec.yml")
29237
+ },
29238
+ // GitHub Workflows
29239
+ {
29240
+ type: "directory",
29241
+ path: ".github/workflows"
29242
+ },
29243
+ {
29244
+ type: "file",
29245
+ path: ".github/workflows/assign-copilot.yml",
29246
+ content: readCliTemplateFile(".github/workflows/assign-copilot.yml")
29247
+ },
29248
+ {
29249
+ type: "file",
29250
+ path: ".github/workflows/ci.yml",
29251
+ content: readCliTemplateFile(".github/workflows/ci.yml")
29252
+ },
29253
+ // Specs directory
29254
+ {
29255
+ type: "directory",
29256
+ path: ".github/specs"
29257
+ },
29258
+ {
29259
+ type: "file",
29260
+ path: ".github/specs/README.md",
29261
+ content: readCliTemplateFile(".github/specs/README.md")
29262
+ },
29263
+ // VSCode configuration
29264
+ {
29265
+ type: "directory",
29266
+ path: ".vscode"
29267
+ },
29268
+ {
29269
+ type: "file",
29270
+ path: ".vscode/extensions.json",
29271
+ content: readCliTemplateFile(".vscode/extensions.json")
29272
+ },
29273
+ {
29274
+ type: "file",
29275
+ path: ".vscode/launch.json",
29276
+ content: readCliTemplateFile(".vscode/launch.json")
29277
+ },
29278
+ {
29279
+ type: "file",
29280
+ path: ".vscode/mcp.json",
29281
+ content: readCliTemplateFile(".vscode/mcp.json")
29282
+ },
29283
+ // Devcontainer configuration
29284
+ {
29285
+ type: "directory",
29286
+ path: ".devcontainer"
29287
+ },
29288
+ {
29289
+ type: "file",
29290
+ path: ".devcontainer/devcontainer.json",
29291
+ content: readCliTemplateFile(".devcontainer/devcontainer.json")
29292
+ }
29293
+ ]
29294
+ };
29295
+ return cachedCliStructure;
29296
+ }
29133
29297
  /**
29134
29298
  * Helper function to read template file content
29135
29299
  * @throws Error if template file cannot be read
@@ -29463,20 +29627,16 @@ const RESTAPI_TEMPLATE_DIR = (0,external_node_path_.join)(PROJECT_ROOT, "api", "
29463
29627
  /**
29464
29628
  * Loads the configuration for the init command
29465
29629
  * Falls back to defaults if no configuration is found
29466
- * Note: webapp structure is lazy-loaded only when requested
29630
+ * Note: project structures are lazy-loaded only when requested
29467
29631
  */ async function loadInitConfig() {
29468
29632
  const { config } = await loadConfig({
29469
29633
  name: "lousy-agents",
29470
29634
  defaults: {
29471
- structures: {
29472
- cli: DEFAULT_CLI_STRUCTURE
29473
- }
29635
+ structures: {}
29474
29636
  }
29475
29637
  });
29476
29638
  return config || {
29477
- structures: {
29478
- cli: DEFAULT_CLI_STRUCTURE
29479
- }
29639
+ structures: {}
29480
29640
  };
29481
29641
  }
29482
29642
  /**
@@ -29493,9 +29653,9 @@ const RESTAPI_TEMPLATE_DIR = (0,external_node_path_.join)(PROJECT_ROOT, "api", "
29493
29653
  if (projectType === "api") {
29494
29654
  return config.structures?.api || buildRestApiStructure();
29495
29655
  }
29496
- // CLI has a default structure
29656
+ // Lazy-load CLI structure only when requested
29497
29657
  if (projectType === "cli") {
29498
- return config.structures?.cli || DEFAULT_CLI_STRUCTURE;
29658
+ return config.structures?.cli || buildCliStructure();
29499
29659
  }
29500
29660
  // GraphQL is not yet implemented
29501
29661
  throw new Error(`Project type "${projectType}" is not yet supported. Supported types: cli, webapp, api`);
@@ -29678,18 +29838,18 @@ function compileBody(buff) {
29678
29838
  let returnStr = "";
29679
29839
  for (; i < buffLength; i++) {
29680
29840
  const currentBlock = buff[i];
29681
- if (typeof currentBlock === "string") returnStr += "__eta.res+='" + currentBlock + "'\n";
29841
+ if (typeof currentBlock === "string") returnStr += "__eta.res+='" + currentBlock + "';\n";
29682
29842
  else {
29683
29843
  const type = currentBlock.t;
29684
29844
  let content = currentBlock.val || "";
29685
29845
  if (config.debug) returnStr += "__eta.line=" + currentBlock.lineNo + "\n";
29686
29846
  if (type === "r") {
29687
29847
  if (config.autoFilter) content = "__eta.f(" + content + ")";
29688
- returnStr += "__eta.res+=" + content + "\n";
29848
+ returnStr += "__eta.res+=" + content + ";\n";
29689
29849
  } else if (type === "i") {
29690
29850
  if (config.autoFilter) content = "__eta.f(" + content + ")";
29691
29851
  if (config.autoEscape) content = "__eta.e(" + content + ")";
29692
- returnStr += "__eta.res+=" + content + "\n";
29852
+ returnStr += "__eta.res+=" + content + ";\n";
29693
29853
  } else if (type === "e") returnStr += content + "\n";
29694
29854
  }
29695
29855
  }
@@ -30213,8 +30373,26 @@ const ProjectTypeSchema = schemas_enum([
30213
30373
  const PROJECT_TYPE_OPTIONS = ProjectTypeSchema.options;
30214
30374
  const SUPPORTED_PROJECT_TYPES = [
30215
30375
  "webapp",
30216
- "api"
30376
+ "api",
30377
+ "cli"
30217
30378
  ];
30379
+ const PROJECT_TYPE_CONFIGS = {
30380
+ cli: {
30381
+ label: "CLI",
30382
+ placeholder: "my-cli",
30383
+ structureKey: "cli"
30384
+ },
30385
+ webapp: {
30386
+ label: "webapp",
30387
+ placeholder: "my-webapp",
30388
+ structureKey: "webapp"
30389
+ },
30390
+ api: {
30391
+ label: "REST API",
30392
+ placeholder: "my-rest-api",
30393
+ structureKey: "api"
30394
+ }
30395
+ };
30218
30396
  const initArgs = {
30219
30397
  kind: {
30220
30398
  type: "string",
@@ -30247,23 +30425,18 @@ async function getValidatedProjectName(promptFn, existingName, projectTypeLabel,
30247
30425
  projectName
30248
30426
  };
30249
30427
  }
30250
- async function createWebappScaffolding(targetDir, templateContext) {
30428
+ async function scaffoldProject(projectType, targetDir, templateContext) {
30429
+ const config = PROJECT_TYPE_CONFIGS[projectType];
30251
30430
  try {
30252
- const webappStructure = await getProjectStructure("webapp");
30253
- await createFilesystemStructure(webappStructure, targetDir, templateContext);
30431
+ const structure = await getProjectStructure(config.structureKey);
30432
+ await createFilesystemStructure(structure, targetDir, templateContext);
30254
30433
  } catch (error) {
30255
- consola.error(`Failed to create webapp scaffolding: ${formatErrorMessage(error)}`);
30434
+ consola.error(`Failed to create ${config.label} scaffolding: ${formatErrorMessage(error)}`);
30256
30435
  throw error;
30257
30436
  }
30258
30437
  }
30259
- async function createRestApiScaffolding(targetDir, templateContext) {
30260
- try {
30261
- const restApiStructure = await getProjectStructure("api");
30262
- await createFilesystemStructure(restApiStructure, targetDir, templateContext);
30263
- } catch (error) {
30264
- consola.error(`Failed to create REST API scaffolding: ${formatErrorMessage(error)}`);
30265
- throw error;
30266
- }
30438
+ function isSupportedProjectType(projectType) {
30439
+ return SUPPORTED_PROJECT_TYPES.includes(projectType);
30267
30440
  }
30268
30441
  const initCommand = defineCommand({
30269
30442
  meta: {
@@ -30286,25 +30459,17 @@ const initCommand = defineCommand({
30286
30459
  }
30287
30460
  const projectType = parseResult.data;
30288
30461
  consola.success(`Selected project type: ${projectType}`);
30289
- if (projectType === "cli") {
30290
- throw new Error('Project type "cli" is not yet supported. Supported types: webapp, api');
30291
- } else if (projectType === "webapp") {
30292
- const { projectName } = await getValidatedProjectName(promptFn, context.args.name, "webapp", "my-webapp");
30293
- const templateContext = {
30294
- projectName
30295
- };
30296
- await createWebappScaffolding(targetDir, templateContext);
30297
- consola.info("Webapp project scaffolding complete. Run 'npm install' to install dependencies.");
30298
- } else if (projectType === "api") {
30299
- const { projectName } = await getValidatedProjectName(promptFn, context.args.name, "REST API", "my-rest-api");
30300
- const templateContext = {
30301
- projectName
30302
- };
30303
- await createRestApiScaffolding(targetDir, templateContext);
30304
- consola.info("REST API project scaffolding complete. Run 'npm install' to install dependencies.");
30305
- } else {
30306
- throw new Error('Project type "graphql" is not yet supported. Supported types: webapp, api');
30307
- }
30462
+ if (!isSupportedProjectType(projectType)) {
30463
+ const supported = SUPPORTED_PROJECT_TYPES.join(", ");
30464
+ throw new Error(`Project type "${projectType}" is not yet supported. Supported types: ${supported}`);
30465
+ }
30466
+ const config = PROJECT_TYPE_CONFIGS[projectType];
30467
+ const { projectName } = await getValidatedProjectName(promptFn, context.args.name, config.label, config.placeholder);
30468
+ const templateContext = {
30469
+ projectName
30470
+ };
30471
+ await scaffoldProject(projectType, targetDir, templateContext);
30472
+ consola.info(`${config.label} project scaffolding complete. Run 'npm install' to install dependencies.`);
30308
30473
  }
30309
30474
  });
30310
30475