@microsoft/agentrc 2.0.1-2 → 2.0.1-4

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/dist/index.js CHANGED
@@ -1,4 +1,6 @@
1
1
  #!/usr/bin/env node
2
+ import { createRequire as __bannerCreateRequire } from "node:module";
3
+ const require = __bannerCreateRequire(import.meta.url);
2
4
  var __create = Object.create;
3
5
  var __defProp = Object.defineProperty;
4
6
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -5357,7 +5359,8 @@ var PACKAGE_MANAGERS = [
5357
5359
  { file: "pnpm-lock.yaml", name: "pnpm" },
5358
5360
  { file: "yarn.lock", name: "yarn" },
5359
5361
  { file: "package-lock.json", name: "npm" },
5360
- { file: "bun.lockb", name: "bun" }
5362
+ { file: "bun.lockb", name: "bun" },
5363
+ { file: "packages.lock.json", name: "nuget" }
5361
5364
  ];
5362
5365
  async function analyzeRepo(repoPath) {
5363
5366
  const files = await safeReadDir(repoPath);
@@ -5379,9 +5382,11 @@ async function analyzeRepo(repoPath) {
5379
5382
  const hasRequirements = files.includes("requirements.txt");
5380
5383
  const hasGoMod = files.includes("go.mod");
5381
5384
  const hasCargo = files.includes("Cargo.toml");
5382
- const hasCsproj = files.some(
5383
- (f) => f.endsWith(".csproj") || f.endsWith(".sln") || f.endsWith(".slnx")
5385
+ const hasDotnet = files.some(
5386
+ (f) => f.endsWith(".csproj") || f.endsWith(".fsproj") || f.endsWith(".sln") || f.endsWith(".slnx") || f === "global.json" || f === "Directory.Build.props"
5384
5387
  );
5388
+ const hasCsproj = hasDotnet && files.some((f) => f.endsWith(".csproj"));
5389
+ const hasFsproj = files.some((f) => f.endsWith(".fsproj"));
5385
5390
  const hasPomXml = files.includes("pom.xml");
5386
5391
  const hasBuildGradle = files.includes("build.gradle") || files.includes("build.gradle.kts");
5387
5392
  const hasGemfile = files.includes("Gemfile");
@@ -5396,7 +5401,8 @@ async function analyzeRepo(repoPath) {
5396
5401
  if (hasPyProject || hasRequirements) analysis.languages.push("Python");
5397
5402
  if (hasGoMod) analysis.languages.push("Go");
5398
5403
  if (hasCargo) analysis.languages.push("Rust");
5399
- if (hasCsproj) analysis.languages.push("C#");
5404
+ if (hasCsproj || hasDotnet && !hasFsproj) analysis.languages.push("C#");
5405
+ if (hasFsproj) analysis.languages.push("F#");
5400
5406
  if (hasPomXml || hasBuildGradle) analysis.languages.push("Java");
5401
5407
  if (hasGemfile) analysis.languages.push("Ruby");
5402
5408
  if (hasComposerJson) analysis.languages.push("PHP");
@@ -5412,6 +5418,10 @@ async function analyzeRepo(repoPath) {
5412
5418
  });
5413
5419
  analysis.frameworks.push(...detectFrameworks(deps, files));
5414
5420
  }
5421
+ if (hasDotnet) {
5422
+ const dotnetFrameworks = await detectDotnetFrameworks(repoPath);
5423
+ analysis.frameworks.push(...dotnetFrameworks);
5424
+ }
5415
5425
  const workspace = await detectWorkspace(repoPath, files, rootPackageJson);
5416
5426
  if (workspace) {
5417
5427
  analysis.workspaceType = workspace.type;
@@ -5472,6 +5482,44 @@ function detectFrameworks(deps, files) {
5472
5482
  if (deps.includes("fastify")) frameworks.push("Fastify");
5473
5483
  return frameworks;
5474
5484
  }
5485
+ async function detectDotnetFrameworks(repoPath) {
5486
+ const projectFiles = await fg("**/*.{csproj,fsproj}", {
5487
+ cwd: repoPath,
5488
+ onlyFiles: true,
5489
+ ignore: ["**/node_modules/**", "**/bin/**", "**/obj/**"]
5490
+ });
5491
+ const frameworks = [];
5492
+ for (const projFile of projectFiles) {
5493
+ try {
5494
+ const content = await fs2.readFile(path2.join(repoPath, projFile), "utf8");
5495
+ frameworks.push(...parseDotnetProject(content));
5496
+ } catch {
5497
+ }
5498
+ }
5499
+ return frameworks;
5500
+ }
5501
+ function parseDotnetProject(content) {
5502
+ const frameworks = [];
5503
+ const hasPackage = (pkg) => content.includes(`Include="${pkg}"`);
5504
+ if (content.includes('Sdk="Microsoft.NET.Sdk.Web"')) frameworks.push("ASP.NET Core");
5505
+ if (content.includes('Sdk="Microsoft.NET.Sdk.BlazorWebAssembly"'))
5506
+ frameworks.push("Blazor WebAssembly");
5507
+ if (hasPackage("Microsoft.AspNetCore") || hasPackage("Microsoft.AspNetCore.App"))
5508
+ frameworks.push("ASP.NET Core");
5509
+ if (hasPackage("Microsoft.AspNetCore.Components")) frameworks.push("Blazor");
5510
+ if (hasPackage("Microsoft.EntityFrameworkCore")) frameworks.push("Entity Framework");
5511
+ if (hasPackage("Microsoft.Maui.Controls")) frameworks.push(".NET MAUI");
5512
+ if (hasPackage("Xamarin.Forms") || hasPackage("Xamarin.Essentials")) frameworks.push("Xamarin");
5513
+ if (content.includes("<UseWPF>true</UseWPF>")) frameworks.push("WPF");
5514
+ if (content.includes("<UseWindowsForms>true</UseWindowsForms>")) frameworks.push("Windows Forms");
5515
+ if (hasPackage("xunit") || hasPackage("xunit.core")) frameworks.push("xUnit");
5516
+ if (hasPackage("NUnit") || hasPackage("nunit.framework")) frameworks.push("NUnit");
5517
+ if (hasPackage("MSTest.TestFramework")) frameworks.push("MSTest");
5518
+ if (frameworks.length === 0 && content.includes("<OutputType>Exe</OutputType>") && content.includes('Sdk="Microsoft.NET.Sdk"')) {
5519
+ frameworks.push("Console");
5520
+ }
5521
+ return frameworks;
5522
+ }
5475
5523
  async function safeReadFile(filePath) {
5476
5524
  try {
5477
5525
  return await fs2.readFile(filePath, "utf8");
@@ -6631,9 +6679,9 @@ import path9 from "path";
6631
6679
  // packages/core/src/utils/pr.ts
6632
6680
  function buildInstructionsPrBody() {
6633
6681
  return [
6634
- "## \u{1F916} Copilot Instructions Added",
6682
+ "## \u{1F916} Instructions Added",
6635
6683
  "",
6636
- "This PR adds a `.github/copilot-instructions.md` file to help GitHub Copilot understand this codebase better.",
6684
+ "This PR adds a `.github/copilot-instructions.md` file to help AI coding assistants understand this codebase better.",
6637
6685
  "",
6638
6686
  "### What's Included",
6639
6687
  "",
@@ -6645,7 +6693,7 @@ function buildInstructionsPrBody() {
6645
6693
  "",
6646
6694
  "### Benefits",
6647
6695
  "",
6648
- "With these instructions, Copilot will:",
6696
+ "With these instructions, AI coding assistants will:",
6649
6697
  "- Generate more contextually-aware code suggestions",
6650
6698
  "- Follow project-specific patterns and conventions",
6651
6699
  "- Understand the codebase structure",
@@ -6664,7 +6712,7 @@ function buildFullPrBody() {
6664
6712
  "",
6665
6713
  "| File | Purpose |",
6666
6714
  "|------|---------|",
6667
- "| `.github/copilot-instructions.md` | Custom instructions for GitHub Copilot |",
6715
+ "| `.github/copilot-instructions.md` | Custom instructions for AI coding assistants |",
6668
6716
  "| `.vscode/settings.json` | VS Code settings for optimal AI assistance |",
6669
6717
  "| `.vscode/mcp.json` | Model Context Protocol server configuration |",
6670
6718
  "",
@@ -6678,7 +6726,7 @@ function buildFullPrBody() {
6678
6726
  "",
6679
6727
  "### Benefits",
6680
6728
  "",
6681
- "With these configurations, Copilot will:",
6729
+ "With these configurations, AI coding assistants will:",
6682
6730
  "- Generate more contextually-aware code suggestions",
6683
6731
  "- Follow project-specific patterns and conventions",
6684
6732
  "- Understand the codebase structure",
@@ -6688,7 +6736,7 @@ function buildFullPrBody() {
6688
6736
  "",
6689
6737
  "1. Merge this PR",
6690
6738
  "2. Open the project in VS Code",
6691
- "3. Start chatting with Copilot \u2014 it now understands your project!",
6739
+ "3. Start using AI assistants \u2014 they now understand your project!",
6692
6740
  "",
6693
6741
  "---",
6694
6742
  "*Generated by [AgentRC](https://github.com/microsoft/agentrc) - Prime your repos for AI*"
@@ -7163,7 +7211,9 @@ function normalizeError(error) {
7163
7211
  }
7164
7212
  function shouldFallbackToExternalServer(error) {
7165
7213
  const message = normalizeError(error).message.toLowerCase();
7166
- return message.includes("unknown option '--headless'") || message.includes("unknown option '--no-auto-update'") || message.includes("copilot cli not found");
7214
+ return message.includes("unknown option '--headless'") || message.includes("unknown option '--no-auto-update'") || // SDK's internal CLI resolution couldn't find the binary
7215
+ message.includes("copilot cli not found") || // Node's spawn() can't execute .bat/.cmd directly on Windows
7216
+ message.includes("spawn einval");
7167
7217
  }
7168
7218
  async function startExternalServer(cliConfig) {
7169
7219
  const [cmd, args2] = buildExecArgs(cliConfig, ["--headless", "--log-level", "debug"]);
@@ -7281,8 +7331,11 @@ async function createCopilotClient(cliConfig) {
7281
7331
  const desc = cliConfig.cliArgs ? `${cliConfig.cliPath} ${cliConfig.cliArgs.join(" ")}` : cliConfig.cliPath;
7282
7332
  logCopilotDebug(`creating SDK client with cliPath=${desc} useStdio=false`);
7283
7333
  const isNpx = /\bnpx(?:\.cmd)?$/iu.test(cliConfig.cliPath);
7284
- if (isNpx) {
7285
- logCopilotDebug("npx wrapper detected; using external server mode");
7334
+ const isBatShim = process.platform === "win32" && /\.(?:bat|cmd)$/iu.test(cliConfig.cliPath);
7335
+ if (isNpx || isBatShim) {
7336
+ logCopilotDebug(
7337
+ `${isNpx ? "npx wrapper" : ".bat/.cmd shim"} detected; using external server mode directly`
7338
+ );
7286
7339
  const external = await startExternalServer(cliConfig);
7287
7340
  const client = new sdk.CopilotClient({ cliUrl: external.cliUrl });
7288
7341
  try {
@@ -7532,7 +7585,7 @@ async function generateCopilotInstructions(options) {
7532
7585
 
7533
7586
  Fan out multiple Explore subagents to map out the codebase in parallel:
7534
7587
  1. Check for existing instruction files: glob for **/{.github/copilot-instructions.md,AGENT.md,CLAUDE.md,.cursorrules,README.md}
7535
- 2. Identify the tech stack: look at package.json, tsconfig.json, pyproject.toml, Cargo.toml, go.mod, *.csproj, *.sln, build.gradle, pom.xml, etc.
7588
+ 2. Identify the tech stack: look at package.json, tsconfig.json, pyproject.toml, Cargo.toml, go.mod, *.csproj, *.fsproj, *.sln, global.json, build.gradle, pom.xml, etc.
7536
7589
  3. Understand the structure: list key directories
7537
7590
  4. Detect monorepo structures: check for workspace configs (npm/pnpm/yarn workspaces, Cargo.toml [workspace], go.work, .sln solution files, settings.gradle include directives, pom.xml modules)
7538
7591
 
@@ -7578,7 +7631,7 @@ async function generateAreaInstructions(options) {
7578
7631
  const applyToStr = applyToPatterns.join(", ");
7579
7632
  progress(`Creating session for area "${area.name}"...`);
7580
7633
  const preferredModel = options.model ?? DEFAULT_MODEL;
7581
- const areaSystemContent = hasExistingInstructions ? `You are an expert codebase analyst. Your task is to generate a concise .instructions.md file for a specific area of a codebase. This file will be used as a file-based custom instruction in VS Code Copilot, automatically applied when working on files matching certain patterns. This file should complement, not duplicate, existing instruction files. Use the Explore subagents and read-only tools to explore the codebase. When done, call the emit_file_content tool with the final markdown.` : `You are an expert codebase analyst. Your task is to generate a concise .instructions.md file for a specific area of a codebase. This file will be used as a file-based custom instruction in VS Code Copilot, automatically applied when working on files matching certain patterns. Use the Explore subagents and read-only tools to explore the codebase. When done, call the emit_file_content tool with the final markdown.`;
7634
+ const areaSystemContent = hasExistingInstructions ? `You are an expert codebase analyst. Your task is to generate a concise .instructions.md file for a specific area of a codebase. This file will be used as an area instruction in VS Code, automatically applied when working on files matching certain patterns. This file should complement, not duplicate, existing instruction files. Use the Explore subagents and read-only tools to explore the codebase. When done, call the emit_file_content tool with the final markdown.` : `You are an expert codebase analyst. Your task is to generate a concise .instructions.md file for a specific area of a codebase. This file will be used as an area instruction in VS Code, automatically applied when working on files matching certain patterns. Use the Explore subagents and read-only tools to explore the codebase. When done, call the emit_file_content tool with the final markdown.`;
7582
7635
  const { tool: emitTool, getContent } = await createEmitTool();
7583
7636
  const session = await client.createSession({
7584
7637
  model: preferredModel,
@@ -7611,7 +7664,7 @@ async function generateAreaInstructions(options) {
7611
7664
  sessionError = getSessionError(errorMsg);
7612
7665
  }
7613
7666
  });
7614
- const prompt = `Analyze the "${area.name}" area of this codebase and generate a file-based instruction file.
7667
+ const prompt = `Analyze the "${area.name}" area of this codebase and generate an area instruction file.
7615
7668
 
7616
7669
  This area covers files matching: ${applyToStr}
7617
7670
  ${area.description ? `Description: ${area.description}` : ""}
@@ -8733,7 +8786,7 @@ function getLevelName(level) {
8733
8786
  function getLevelDescription(level) {
8734
8787
  const descriptions = {
8735
8788
  1: "Repo builds, tests run, and basic tooling (linter, lockfile) is in place. AI agents can clone and get started.",
8736
- 2: "README, CONTRIBUTING guide, and custom AI instructions exist. Agents understand project context and conventions.",
8789
+ 2: "README, CONTRIBUTING guide, and custom instructions exist. Agents understand project context and conventions.",
8737
8790
  3: "CI/CD, security policies, CODEOWNERS, and observability are configured. Agents operate within well-defined guardrails.",
8738
8791
  4: "MCP servers, custom agents, and AI skills are set up. Agents have deep integration with project-specific tools and workflows.",
8739
8792
  5: "Full AI-native development: agents can independently plan, implement, test, and ship changes with minimal human oversight."
@@ -9198,7 +9251,7 @@ function buildCriteria() {
9198
9251
  },
9199
9252
  {
9200
9253
  id: "custom-instructions",
9201
- title: "Custom AI instructions or agent guidance",
9254
+ title: "Custom instructions or agent guidance",
9202
9255
  pillar: "ai-tooling",
9203
9256
  level: 1,
9204
9257
  scope: "repo",
@@ -9209,7 +9262,7 @@ function buildCriteria() {
9209
9262
  if (rootFound.length === 0) {
9210
9263
  return {
9211
9264
  status: "fail",
9212
- reason: "Missing custom AI instructions (e.g. copilot-instructions.md, CLAUDE.md, AGENTS.md, .cursorrules).",
9265
+ reason: "Missing custom instructions (e.g. copilot-instructions.md, CLAUDE.md, AGENTS.md, .cursorrules).",
9213
9266
  evidence: [
9214
9267
  "copilot-instructions.md",
9215
9268
  "CLAUDE.md",
@@ -9225,13 +9278,13 @@ function buildCriteria() {
9225
9278
  if (fileBasedInstructions.length === 0) {
9226
9279
  return {
9227
9280
  status: "pass",
9228
- reason: `Root instructions found, but no file-based instructions for ${areas.length} detected areas. Run \`agentrc instructions --areas\` to generate.`,
9281
+ reason: `Root instructions found, but no area instructions for ${areas.length} detected areas. Run \`agentrc instructions --areas\` to generate.`,
9229
9282
  evidence: [...rootFound, ...areas.map((a) => `${a.name}: missing .instructions.md`)]
9230
9283
  };
9231
9284
  }
9232
9285
  return {
9233
9286
  status: "pass",
9234
- reason: `Root + ${fileBasedInstructions.length} file-based instruction(s) found.`,
9287
+ reason: `Root + ${fileBasedInstructions.length} area instruction(s) found.`,
9235
9288
  evidence: [...rootFound, ...fileBasedInstructions]
9236
9289
  };
9237
9290
  }
@@ -9856,7 +9909,7 @@ async function processRepo(params) {
9856
9909
  );
9857
9910
  }
9858
9911
  progress?.update(`${label}: Committing...`);
9859
- await commitAll(repoPath, "chore: add copilot instructions via AgentRC");
9912
+ await commitAll(repoPath, "chore: add instructions via AgentRC");
9860
9913
  progress?.update(`${label}: Pushing...`);
9861
9914
  await pushBranch(repoPath, branch, token, provider);
9862
9915
  progress?.update(`${label}: Creating PR...`);
@@ -9892,7 +9945,7 @@ async function processGitHubRepo(options) {
9892
9945
  token,
9893
9946
  owner: repo.owner,
9894
9947
  repo: repo.name,
9895
- title: "\u{1F916} Add Copilot instructions via AgentRC",
9948
+ title: "\u{1F916} Add instructions via AgentRC",
9896
9949
  body: buildInstructionsPrBody(),
9897
9950
  head: branchName,
9898
9951
  base: repo.defaultBranch
@@ -9924,7 +9977,7 @@ async function processAzureRepo(options) {
9924
9977
  project: repo.project,
9925
9978
  repoId: repo.id,
9926
9979
  repoName: repo.name,
9927
- title: "\u{1F916} Add Copilot instructions via AgentRC",
9980
+ title: "\u{1F916} Add instructions via AgentRC",
9928
9981
  body: buildInstructionsPrBody(),
9929
9982
  sourceBranch: branchName,
9930
9983
  targetBranch: repo.defaultBranch
@@ -11039,11 +11092,7 @@ import path10 from "path";
11039
11092
 
11040
11093
  // packages/core/src/services/visualReport.ts
11041
11094
  function generateVisualReport(options) {
11042
- const {
11043
- reports,
11044
- title = "AI Readiness Report",
11045
- generatedAt = (/* @__PURE__ */ new Date()).toISOString()
11046
- } = options;
11095
+ const { reports, title = "Readiness Report", generatedAt = (/* @__PURE__ */ new Date()).toISOString() } = options;
11047
11096
  const successfulReports = reports.filter((r) => !r.error);
11048
11097
  const failedReports = reports.filter((r) => r.error);
11049
11098
  const totalRepos = reports.length;
@@ -11612,7 +11661,7 @@ function generateVisualReport(options) {
11612
11661
  ` : ""}
11613
11662
 
11614
11663
  <div class="footer">
11615
- <p>Generated with <a href="https://github.com/microsoft/agentrc">AgentRC</a> &middot; AI Readiness Tool</p>
11664
+ <p>Generated with <a href="https://github.com/microsoft/agentrc">AgentRC</a> &middot; Readiness Tool</p>
11616
11665
  </div>
11617
11666
  </div>
11618
11667
  <script>
@@ -12099,7 +12148,7 @@ function BatchReadinessTui({ token, outputPath, policies }) {
12099
12148
  },
12100
12149
  error: r.error
12101
12150
  })),
12102
- title: "Batch AI Readiness Report",
12151
+ title: "Batch Readiness Report",
12103
12152
  generatedAt: (/* @__PURE__ */ new Date()).toISOString()
12104
12153
  });
12105
12154
  const finalOutputPath = outputPath ?? path10.join(process.cwd(), "batch-readiness-report.html");
@@ -13471,6 +13520,7 @@ async function instructionsCommand(options) {
13471
13520
  return;
13472
13521
  }
13473
13522
  try {
13523
+ const dryRunFiles = [];
13474
13524
  if (!options.areasOnly && !options.area) {
13475
13525
  if (strategy === "nested") {
13476
13526
  try {
@@ -13482,25 +13532,57 @@ async function instructionsCommand(options) {
13482
13532
  detailDir,
13483
13533
  claudeMd
13484
13534
  });
13485
- const actions = await writeNestedInstructions(repoPath, nestedResult, options.force);
13486
- for (const action of actions) {
13487
- const relPath = path14.relative(process.cwd(), action.path);
13488
- if (action.action === "wrote") {
13489
- if (shouldLog(options)) progress.succeed(`Wrote ${relPath}`);
13490
- } else if (shouldLog(options)) {
13491
- progress.update(`Skipped ${relPath} (${skipReason(action.action)})`);
13535
+ if (options.dryRun) {
13536
+ const dryFiles = [
13537
+ { path: nestedResult.hub.relativePath, content: nestedResult.hub.content },
13538
+ ...nestedResult.details.map((d) => ({ path: d.relativePath, content: d.content })),
13539
+ ...nestedResult.claudeMd ? [
13540
+ {
13541
+ path: nestedResult.claudeMd.relativePath,
13542
+ content: nestedResult.claudeMd.content
13543
+ }
13544
+ ] : []
13545
+ ];
13546
+ for (const file of dryFiles) {
13547
+ const relPath = path14.relative(process.cwd(), path14.join(repoPath, file.path));
13548
+ if (shouldLog(options)) {
13549
+ progress.update(
13550
+ `[dry-run] Would write ${relPath} (${Buffer.byteLength(file.content, "utf8")} bytes)`
13551
+ );
13552
+ }
13553
+ }
13554
+ if (options.json) {
13555
+ dryRunFiles.push(
13556
+ ...dryFiles.map((f) => ({
13557
+ path: f.path,
13558
+ bytes: Buffer.byteLength(f.content, "utf8")
13559
+ }))
13560
+ );
13561
+ }
13562
+ for (const warning of nestedResult.warnings) {
13563
+ if (shouldLog(options)) progress.update(`Warning: ${warning}`);
13564
+ }
13565
+ } else {
13566
+ const actions = await writeNestedInstructions(repoPath, nestedResult, options.force);
13567
+ for (const action of actions) {
13568
+ const relPath = path14.relative(process.cwd(), action.path);
13569
+ if (action.action === "wrote") {
13570
+ if (shouldLog(options)) progress.succeed(`Wrote ${relPath}`);
13571
+ } else if (shouldLog(options)) {
13572
+ progress.update(`Skipped ${relPath} (${skipReason(action.action)})`);
13573
+ }
13574
+ }
13575
+ for (const warning of nestedResult.warnings) {
13576
+ if (shouldLog(options)) progress.update(`Warning: ${warning}`);
13577
+ }
13578
+ if (options.json) {
13579
+ const result = {
13580
+ ok: true,
13581
+ status: "success",
13582
+ data: { files: actions }
13583
+ };
13584
+ outputResult(result, true);
13492
13585
  }
13493
- }
13494
- for (const warning of nestedResult.warnings) {
13495
- if (shouldLog(options)) progress.update(`Warning: ${warning}`);
13496
- }
13497
- if (options.json) {
13498
- const result = {
13499
- ok: true,
13500
- status: "success",
13501
- data: { files: actions }
13502
- };
13503
- outputResult(result, true);
13504
13586
  }
13505
13587
  } catch (error) {
13506
13588
  const msg = "Failed to generate nested instructions. " + (error instanceof Error ? error.message : String(error));
@@ -13525,36 +13607,48 @@ async function instructionsCommand(options) {
13525
13607
  return;
13526
13608
  }
13527
13609
  if (content) {
13528
- await ensureDir(path14.dirname(outputPath));
13529
- const { wrote, reason } = await safeWriteFile(
13530
- outputPath,
13531
- content,
13532
- Boolean(options.force)
13533
- );
13534
- if (!wrote) {
13535
- const relPath = path14.relative(process.cwd(), outputPath);
13536
- const why = reason === "symlink" ? "path is a symlink" : "file exists (use --force)";
13610
+ if (options.dryRun) {
13611
+ const relPath = path14.relative(repoPath, outputPath);
13612
+ const displayPath = path14.relative(process.cwd(), outputPath);
13613
+ const byteCount = Buffer.byteLength(content, "utf8");
13614
+ if (shouldLog(options)) {
13615
+ progress.update(`[dry-run] Would write ${displayPath} (${byteCount} bytes)`);
13616
+ }
13537
13617
  if (options.json) {
13538
- const result = {
13539
- ok: true,
13540
- status: "noop",
13541
- data: { outputPath, skipped: true, reason: why }
13542
- };
13543
- outputResult(result, true);
13544
- } else if (shouldLog(options)) {
13545
- progress.update(`Skipped ${relPath}: ${why}`);
13618
+ dryRunFiles.push({ path: relPath, bytes: byteCount });
13546
13619
  }
13547
13620
  } else {
13548
- const byteCount = Buffer.byteLength(content, "utf8");
13549
- if (options.json) {
13550
- const result = {
13551
- ok: true,
13552
- status: "success",
13553
- data: { outputPath, model: options.model ?? "default", byteCount }
13554
- };
13555
- outputResult(result, true);
13556
- } else if (shouldLog(options)) {
13557
- progress.succeed(`Updated ${path14.relative(process.cwd(), outputPath)}`);
13621
+ await ensureDir(path14.dirname(outputPath));
13622
+ const { wrote, reason } = await safeWriteFile(
13623
+ outputPath,
13624
+ content,
13625
+ Boolean(options.force)
13626
+ );
13627
+ if (!wrote) {
13628
+ const relPath = path14.relative(process.cwd(), outputPath);
13629
+ const why = reason === "symlink" ? "path is a symlink" : "file exists (use --force)";
13630
+ if (options.json) {
13631
+ const result = {
13632
+ ok: true,
13633
+ status: "noop",
13634
+ data: { outputPath, skipped: true, reason: why }
13635
+ };
13636
+ outputResult(result, true);
13637
+ } else if (shouldLog(options)) {
13638
+ progress.update(`Skipped ${relPath}: ${why}`);
13639
+ }
13640
+ } else {
13641
+ const byteCount = Buffer.byteLength(content, "utf8");
13642
+ if (options.json) {
13643
+ const result = {
13644
+ ok: true,
13645
+ status: "success",
13646
+ data: { outputPath, model: options.model ?? "default", byteCount }
13647
+ };
13648
+ outputResult(result, true);
13649
+ } else if (shouldLog(options)) {
13650
+ progress.succeed(`Updated ${path14.relative(process.cwd(), outputPath)}`);
13651
+ }
13558
13652
  }
13559
13653
  }
13560
13654
  }
@@ -13588,7 +13682,7 @@ async function instructionsCommand(options) {
13588
13682
  return;
13589
13683
  }
13590
13684
  if (shouldLog(options)) {
13591
- progress.update(`Generating file-based instructions for ${targetAreas.length} area(s)...`);
13685
+ progress.update(`Generating instructions for ${targetAreas.length} area(s)...`);
13592
13686
  }
13593
13687
  for (const area of targetAreas) {
13594
13688
  try {
@@ -13608,17 +13702,46 @@ async function instructionsCommand(options) {
13608
13702
  detailDir,
13609
13703
  claudeMd
13610
13704
  });
13611
- const actions = await writeNestedInstructions(repoPath, nestedResult, options.force);
13612
- for (const action of actions) {
13613
- const relPath = path14.relative(process.cwd(), action.path);
13614
- if (action.action === "wrote") {
13615
- if (shouldLog(options)) progress.succeed(`Wrote ${relPath}`);
13616
- } else if (shouldLog(options)) {
13617
- progress.update(`Skipped ${relPath} (${skipReason(action.action)})`);
13705
+ if (options.dryRun) {
13706
+ const dryFiles = [
13707
+ { path: nestedResult.hub.relativePath, content: nestedResult.hub.content },
13708
+ ...nestedResult.details.map((d) => ({ path: d.relativePath, content: d.content })),
13709
+ ...nestedResult.claudeMd ? [
13710
+ {
13711
+ path: nestedResult.claudeMd.relativePath,
13712
+ content: nestedResult.claudeMd.content
13713
+ }
13714
+ ] : []
13715
+ ];
13716
+ for (const file of dryFiles) {
13717
+ const relPath = path14.relative(process.cwd(), path14.join(repoPath, file.path));
13718
+ if (shouldLog(options)) {
13719
+ progress.update(
13720
+ `[dry-run] Would write ${relPath} (${Buffer.byteLength(file.content, "utf8")} bytes)`
13721
+ );
13722
+ }
13723
+ }
13724
+ if (options.json) {
13725
+ dryRunFiles.push(
13726
+ ...dryFiles.map((f) => ({
13727
+ path: f.path,
13728
+ bytes: Buffer.byteLength(f.content, "utf8")
13729
+ }))
13730
+ );
13731
+ }
13732
+ } else {
13733
+ const actions = await writeNestedInstructions(repoPath, nestedResult, options.force);
13734
+ for (const action of actions) {
13735
+ const relPath = path14.relative(process.cwd(), action.path);
13736
+ if (action.action === "wrote") {
13737
+ if (shouldLog(options)) progress.succeed(`Wrote ${relPath}`);
13738
+ } else if (shouldLog(options)) {
13739
+ progress.update(`Skipped ${relPath} (${skipReason(action.action)})`);
13740
+ }
13741
+ }
13742
+ for (const warning of nestedResult.warnings) {
13743
+ if (shouldLog(options)) progress.update(`Warning: ${warning}`);
13618
13744
  }
13619
- }
13620
- for (const warning of nestedResult.warnings) {
13621
- if (shouldLog(options)) progress.update(`Warning: ${warning}`);
13622
13745
  }
13623
13746
  } else {
13624
13747
  const body = await generateAreaInstructions({
@@ -13633,21 +13756,37 @@ async function instructionsCommand(options) {
13633
13756
  }
13634
13757
  continue;
13635
13758
  }
13636
- const result = await writeAreaInstruction(repoPath, area, body, options.force);
13637
- if (result.status === "skipped") {
13759
+ if (options.dryRun) {
13638
13760
  if (shouldLog(options)) {
13639
- progress.update(`Skipped "${area.name}" \u2014 file exists (use --force to overwrite).`);
13761
+ progress.update(
13762
+ `[dry-run] Would write area "${area.name}" (${Buffer.byteLength(body, "utf8")} bytes)`
13763
+ );
13764
+ }
13765
+ if (options.json) {
13766
+ dryRunFiles.push({
13767
+ path: path14.relative(repoPath, areaInstructionPath(repoPath, area)),
13768
+ bytes: Buffer.byteLength(body, "utf8")
13769
+ });
13770
+ }
13771
+ } else {
13772
+ const result = await writeAreaInstruction(repoPath, area, body, options.force);
13773
+ if (result.status === "skipped") {
13774
+ if (shouldLog(options)) {
13775
+ progress.update(
13776
+ `Skipped "${area.name}" \u2014 file exists (use --force to overwrite).`
13777
+ );
13778
+ }
13779
+ continue;
13780
+ }
13781
+ if (result.status === "symlink") {
13782
+ if (shouldLog(options)) {
13783
+ progress.update(`Skipped "${area.name}" \u2014 path is a symlink.`);
13784
+ }
13785
+ continue;
13640
13786
  }
13641
- continue;
13642
- }
13643
- if (result.status === "symlink") {
13644
13787
  if (shouldLog(options)) {
13645
- progress.update(`Skipped "${area.name}" \u2014 path is a symlink.`);
13788
+ progress.succeed(`Wrote ${path14.relative(process.cwd(), result.filePath)}`);
13646
13789
  }
13647
- continue;
13648
- }
13649
- if (shouldLog(options)) {
13650
- progress.succeed(`Wrote ${path14.relative(process.cwd(), result.filePath)}`);
13651
13790
  }
13652
13791
  }
13653
13792
  } catch (error) {
@@ -13659,6 +13798,12 @@ async function instructionsCommand(options) {
13659
13798
  }
13660
13799
  }
13661
13800
  }
13801
+ if (options.dryRun && options.json) {
13802
+ outputResult(
13803
+ { ok: true, status: "noop", data: { dryRun: true, files: dryRunFiles } },
13804
+ true
13805
+ );
13806
+ }
13662
13807
  if (!wantAreas && shouldLog(options) && !options.json) {
13663
13808
  process.stderr.write("\nNext steps:\n");
13664
13809
  process.stderr.write(" agentrc eval --init Scaffold evaluation test cases\n");
@@ -13677,6 +13822,18 @@ async function instructionsCommand(options) {
13677
13822
  async function generateCommand(type, repoPathArg, options) {
13678
13823
  const repoPath = path15.resolve(repoPathArg ?? process.cwd());
13679
13824
  if (type === "instructions" || type === "agents") {
13825
+ if (!options.quiet) {
13826
+ process.stderr.write(
13827
+ `\u26A0 \`generate ${type}\` is deprecated \u2014 use \`agentrc instructions\` directly.
13828
+ `
13829
+ );
13830
+ }
13831
+ if (options.perApp && !options.quiet) {
13832
+ process.stderr.write(
13833
+ `\u26A0 --per-app is deprecated \u2014 use \`agentrc instructions --areas\` instead.
13834
+ `
13835
+ );
13836
+ }
13680
13837
  const output = type === "agents" ? path15.join(repoPath, "AGENTS.md") : void 0;
13681
13838
  await instructionsCommand({
13682
13839
  repo: repoPath,
@@ -13949,7 +14106,7 @@ async function initCommand(repoPathArg, options) {
13949
14106
  `);
13950
14107
  }
13951
14108
  process.stderr.write("\nNext steps:\n");
13952
- process.stderr.write(" agentrc readiness Check AI readiness across 9 pillars\n");
14109
+ process.stderr.write(" agentrc readiness Run readiness report across 9 pillars\n");
13953
14110
  if (analysis.areas && analysis.areas.length > 0) {
13954
14111
  process.stderr.write(" agentrc instructions --areas Generate per-area instructions\n");
13955
14112
  }
@@ -14201,7 +14358,7 @@ async function readinessCommand(repoPathArg, options) {
14201
14358
  if (options.visual || outputExt === ".html") {
14202
14359
  const html = generateVisualReport({
14203
14360
  reports: [{ repo: repoName, report }],
14204
- title: `AI Readiness Report: ${repoName}`,
14361
+ title: `Readiness Report: ${repoName}`,
14205
14362
  generatedAt: (/* @__PURE__ */ new Date()).toISOString()
14206
14363
  });
14207
14364
  const outputPath = options.output ? resolvedOutputPath : path18.join(repoPath, "readiness-report.html");
@@ -14301,7 +14458,7 @@ ${label}`));
14301
14458
  }
14302
14459
  }
14303
14460
  if (report.extras.length) {
14304
- log(chalk3.bold("\nAI readiness extras"));
14461
+ log(chalk3.bold("\nReadiness extras"));
14305
14462
  for (const extra of report.extras) {
14306
14463
  const icon = extra.status === "pass" ? chalk3.green("\u2714") : chalk3.red("\u2716");
14307
14464
  log(`${icon} ${extra.title}`);
@@ -14366,7 +14523,7 @@ function printAreaBreakdown(areaReports) {
14366
14523
  }
14367
14524
  function formatReadinessMarkdown(report, repoName) {
14368
14525
  const lines = [];
14369
- lines.push(`# AI Readiness Report: ${repoName}`);
14526
+ lines.push(`# Readiness Report: ${repoName}`);
14370
14527
  lines.push("");
14371
14528
  lines.push(`**Level ${report.achievedLevel}** \u2014 ${levelName(report.achievedLevel)}`);
14372
14529
  lines.push("");
@@ -14399,7 +14556,7 @@ function formatReadinessMarkdown(report, repoName) {
14399
14556
  lines.push("");
14400
14557
  }
14401
14558
  if (report.extras.length > 0) {
14402
- lines.push("## AI Readiness Extras");
14559
+ lines.push("## Readiness Extras");
14403
14560
  lines.push("");
14404
14561
  for (const extra of report.extras) {
14405
14562
  const icon = extra.status === "pass" ? "\u2705" : "\u274C";
@@ -14554,7 +14711,7 @@ function AgentRCTui({ repoPath, skipAnimation = false }) {
14554
14711
  const [areaCursor, setAreaCursor] = useState5(0);
14555
14712
  const repoLabel = useMemo(() => path19.basename(repoPath), [repoPath]);
14556
14713
  const repoFull = useMemo(() => repoPath, [repoPath]);
14557
- const isLoading = status === "generating" || status === "bootstrapping" || status === "evaluating" || status === "generating-areas";
14714
+ const isLoading = status === "generating" || status === "bootstrapping" || status === "evaluating" || status === "generating-areas" || status === "readiness-running";
14558
14715
  const isMenu = status === "model-pick" || status === "eval-pick" || status === "batch-pick" || status === "generate-pick" || status === "generate-app-pick" || status === "generate-area-pick";
14559
14716
  const spinner = useSpinner(isLoading);
14560
14717
  const addLog = (text, type = "info") => {
@@ -14796,14 +14953,14 @@ function AgentRCTui({ repoPath, skipAnimation = false }) {
14796
14953
  }
14797
14954
  return;
14798
14955
  }
14799
- if (input.toLowerCase() === "f") {
14956
+ if (input.toLowerCase() === "n") {
14800
14957
  if (repoAreas.length === 0) {
14801
14958
  setMessage("No areas detected. Add agentrc.config.json to define areas.");
14802
14959
  return;
14803
14960
  }
14804
14961
  setAreaCursor(0);
14805
14962
  setStatus("generate-area-pick");
14806
- setMessage("Generate file-based instructions for areas.");
14963
+ setMessage("Generate nested instructions for areas.");
14807
14964
  return;
14808
14965
  }
14809
14966
  if (key.escape) {
@@ -14866,10 +15023,7 @@ function AgentRCTui({ repoPath, skipAnimation = false }) {
14866
15023
  if (status === "generate-area-pick") {
14867
15024
  if (input.toLowerCase() === "a") {
14868
15025
  setStatus("generating-areas");
14869
- addLog(
14870
- `Generating file-based instructions for ${repoAreas.length} areas...`,
14871
- "progress"
14872
- );
15026
+ addLog(`Generating nested instructions for ${repoAreas.length} areas...`, "progress");
14873
15027
  let written = 0;
14874
15028
  for (const [i, area] of repoAreas.entries()) {
14875
15029
  setMessage(`Generating for "${area.name}" (${i + 1}/${repoAreas.length})...`);
@@ -14892,9 +15046,7 @@ function AgentRCTui({ repoPath, skipAnimation = false }) {
14892
15046
  }
14893
15047
  }
14894
15048
  setStatus("done");
14895
- setMessage(
14896
- `Generated file-based instructions for ${written}/${repoAreas.length} areas.`
14897
- );
15049
+ setMessage(`Generated nested instructions for ${written}/${repoAreas.length} areas.`);
14898
15050
  return;
14899
15051
  }
14900
15052
  if (key.upArrow) {
@@ -14910,7 +15062,7 @@ function AgentRCTui({ repoPath, skipAnimation = false }) {
14910
15062
  if (!area) return;
14911
15063
  setStatus("generating-areas");
14912
15064
  setMessage(`Generating for "${area.name}"...`);
14913
- addLog(`Generating file-based instructions for "${area.name}"...`, "progress");
15065
+ addLog(`Generating nested instructions for "${area.name}"...`, "progress");
14914
15066
  try {
14915
15067
  const body = await generateAreaInstructions({
14916
15068
  repoPath,
@@ -15083,6 +15235,36 @@ function AgentRCTui({ repoPath, skipAnimation = false }) {
15083
15235
  setMessage("Select what to generate.");
15084
15236
  return;
15085
15237
  }
15238
+ if (input.toLowerCase() === "r") {
15239
+ setStatus("readiness-running");
15240
+ setMessage("Running readiness report\u2026");
15241
+ addLog("Running readiness report\u2026", "progress");
15242
+ try {
15243
+ let policies;
15244
+ try {
15245
+ const config = await loadAgentrcConfig(repoPath);
15246
+ policies = config?.policies;
15247
+ } catch {
15248
+ }
15249
+ const report = await runReadinessReport({ repoPath, policies });
15250
+ const levelName2 = getLevelName(report.achievedLevel);
15251
+ const failCount = report.criteria.filter((c) => c.status === "fail").length;
15252
+ addLog(
15253
+ `Level ${report.achievedLevel} (${levelName2}) \u2014 ${failCount} item(s) to fix`,
15254
+ "success"
15255
+ );
15256
+ setStatus("done");
15257
+ setMessage(
15258
+ `Readiness: Level ${report.achievedLevel} (${levelName2}). ${failCount} improvement(s) available.`
15259
+ );
15260
+ } catch (error) {
15261
+ const msg = error instanceof Error ? error.message : "Failed.";
15262
+ addLog(`Readiness failed: ${msg}`, "error");
15263
+ setStatus("error");
15264
+ setMessage(`Readiness failed: ${msg}`);
15265
+ }
15266
+ return;
15267
+ }
15086
15268
  if (input.toLowerCase() === "b") {
15087
15269
  setStatus("batch-pick");
15088
15270
  setMessage("Select batch provider.");
@@ -15135,7 +15317,7 @@ function AgentRCTui({ repoPath, skipAnimation = false }) {
15135
15317
  { isActive: inputActive }
15136
15318
  );
15137
15319
  const statusIcon = status === "error" ? accessible ? "ERROR" : "\u2717" : status === "done" ? accessible ? "OK" : "\u2713" : isLoading ? spinner : accessible ? "*" : "\u25CF";
15138
- const statusLabel = status === "intro" ? "starting" : status === "idle" ? "ready" : status === "bootstrapEvalCount" ? "input" : status === "bootstrapEvalConfirm" ? "confirm" : status === "eval-pick" ? "eval" : status === "batch-pick" ? "batch" : status === "model-pick" ? "models" : status;
15320
+ const statusLabel = status === "intro" ? "starting" : status === "idle" ? "ready" : status === "bootstrapEvalCount" ? "input" : status === "bootstrapEvalConfirm" ? "confirm" : status === "eval-pick" ? "eval" : status === "batch-pick" ? "batch" : status === "model-pick" ? "models" : status === "readiness-running" ? "readiness" : status;
15139
15321
  const statusColor = status === "error" ? "red" : status === "done" ? "green" : isLoading ? "yellow" : isMenu ? "magentaBright" : "cyanBright";
15140
15322
  const formatTokens = (result) => {
15141
15323
  const withUsage = result.metrics?.withInstructions?.tokenUsage;
@@ -15287,14 +15469,7 @@ function AgentRCTui({ repoPath, skipAnimation = false }) {
15287
15469
  ] }, app2.name)) })
15288
15470
  ] }),
15289
15471
  status === "generate-area-pick" && repoAreas.length > 0 && /* @__PURE__ */ jsxs4(Fragment, { children: [
15290
- /* @__PURE__ */ jsx7(
15291
- Divider,
15292
- {
15293
- columns: terminalColumns,
15294
- label: "File-based instructions",
15295
- accessible
15296
- }
15297
- ),
15472
+ /* @__PURE__ */ jsx7(Divider, { columns: terminalColumns, label: "Nested instructions", accessible }),
15298
15473
  /* @__PURE__ */ jsx7(Box5, { flexDirection: "column", paddingLeft: 1, children: repoAreas.map((area, i) => /* @__PURE__ */ jsxs4(Text5, { children: [
15299
15474
  /* @__PURE__ */ jsx7(Text5, { color: i === areaCursor ? "cyanBright" : "gray", children: i === areaCursor ? accessible ? ">" : "\u25B6" : " " }),
15300
15475
  /* @__PURE__ */ jsx7(Text5, { color: "gray", children: " " }),
@@ -15375,9 +15550,9 @@ function AgentRCTui({ repoPath, skipAnimation = false }) {
15375
15550
  /* @__PURE__ */ jsx7(Text5, { color: "cyan", children: " to select " }),
15376
15551
  /* @__PURE__ */ jsx7(KeyHint, { k: "Esc", label: "Back" })
15377
15552
  ] }) : status === "generate-pick" ? /* @__PURE__ */ jsxs4(Box5, { children: [
15378
- /* @__PURE__ */ jsx7(KeyHint, { k: "C", label: "Copilot instructions" }),
15379
- /* @__PURE__ */ jsx7(KeyHint, { k: "A", label: "AGENTS.md" }),
15380
- repoAreas.length > 0 && /* @__PURE__ */ jsx7(KeyHint, { k: "F", label: "File-based (areas)" }),
15553
+ /* @__PURE__ */ jsx7(KeyHint, { k: "C", label: "Instructions" }),
15554
+ /* @__PURE__ */ jsx7(KeyHint, { k: "A", label: "Agents" }),
15555
+ repoAreas.length > 0 && /* @__PURE__ */ jsx7(KeyHint, { k: "N", label: "Nested (areas)" }),
15381
15556
  /* @__PURE__ */ jsx7(KeyHint, { k: "Esc", label: "Back" })
15382
15557
  ] }) : status === "generate-app-pick" ? /* @__PURE__ */ jsxs4(Box5, { children: [
15383
15558
  /* @__PURE__ */ jsx7(KeyHint, { k: "R", label: "Root only" }),
@@ -15413,6 +15588,7 @@ function AgentRCTui({ repoPath, skipAnimation = false }) {
15413
15588
  ] }) : isLoading ? /* @__PURE__ */ jsx7(Box5, { children: /* @__PURE__ */ jsx7(KeyHint, { k: "Q", label: "Quit" }) }) : /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", children: [
15414
15589
  /* @__PURE__ */ jsxs4(Box5, { children: [
15415
15590
  /* @__PURE__ */ jsx7(KeyHint, { k: "G", label: "Generate" }),
15591
+ /* @__PURE__ */ jsx7(KeyHint, { k: "R", label: "Readiness" }),
15416
15592
  /* @__PURE__ */ jsx7(KeyHint, { k: "E", label: "Eval" }),
15417
15593
  /* @__PURE__ */ jsx7(KeyHint, { k: "B", label: "Batch" })
15418
15594
  ] }),
@@ -15466,7 +15642,7 @@ function withGlobalOpts(fn) {
15466
15642
  function runCli(argv) {
15467
15643
  const program = new Command();
15468
15644
  program.name("agentrc").description("Set up repositories for AI-assisted development").version(CLI_VERSION).option("--json", "Output machine-readable JSON to stdout").option("--quiet", "Suppress stderr progress output").option("--accessible", "Enable screen reader friendly output");
15469
- program.command("init").description("Interactive repo setup \u2014 analyze, generate instructions and configs").argument("[path]", "Path to a local repository").option("--github", "Use a GitHub repository").option("--provider <provider>", "Repo provider (github|azure)").option("--yes", "Accept defaults (generates instructions, MCP, and VS Code configs)").option("--force", "Overwrite existing files").option("--model <name>", "Model for instructions generation", DEFAULT_MODEL).action(withGlobalOpts(initCommand));
15645
+ program.command("init").description("Init repository \u2014 analyze & generate instructions").argument("[path]", "Path to a local repository").option("--github", "Use a GitHub repository").option("--provider <provider>", "Repo provider (github|azure)").option("--yes", "Accept defaults (generates instructions, MCP, and VS Code configs)").option("--force", "Overwrite existing files").option("--model <name>", "Model for instructions generation", DEFAULT_MODEL).action(withGlobalOpts(initCommand));
15470
15646
  program.command("analyze").description("Detect languages, frameworks, monorepo structure, and areas").argument("[path]", "Path to a local repository").option("--output <path>", "Write report to file (.json or .md)").option("--force", "Overwrite existing output file").action(withGlobalOpts(analyzeCommand));
15471
15647
  program.command("generate").description("Generate instructions, agents, MCP, or VS Code configs").addArgument(
15472
15648
  new Argument("<type>", "Config type to generate").choices([
@@ -15475,14 +15651,14 @@ function runCli(argv) {
15475
15651
  "mcp",
15476
15652
  "vscode"
15477
15653
  ])
15478
- ).argument("[path]", "Path to a local repository").option("--force", "Overwrite existing files").option("--per-app", "Generate per-app in monorepos").option("--model <name>", "Model for instructions generation", DEFAULT_MODEL).option("--strategy <mode>", "Instruction strategy (flat or nested)").action(withGlobalOpts(generateCommand));
15654
+ ).argument("[path]", "Path to a local repository").option("--force", "Overwrite existing files").option("--per-app", "(deprecated) Use `agentrc instructions --areas` instead").option("--model <name>", "Model for instructions generation", DEFAULT_MODEL).option("--strategy <mode>", "Instruction strategy (flat or nested)").action(withGlobalOpts(generateCommand));
15479
15655
  program.command("pr").description("Create a PR with generated configs on GitHub or Azure DevOps").argument("[repo]", "Repo identifier (github: owner/name, azure: org/project/repo)").option("--branch <name>", "Branch name").option("--provider <provider>", "Repo provider (github|azure)").option("--model <name>", "Model for instructions generation", DEFAULT_MODEL).action(withGlobalOpts(prCommand));
15480
15656
  program.command("eval").description("Compare AI responses with and without instructions").argument("[path]", "Path to eval config JSON").option("--repo <path>", "Repository path", process.cwd()).option("--model <name>", "Model for responses", DEFAULT_MODEL).option("--judge-model <name>", "Model for judging", DEFAULT_JUDGE_MODEL).option("--list-models", "List Copilot CLI models and exit").option("--output <path>", "Write results JSON to file").option("--init", "Create a starter agentrc.eval.json file").option("--count <number>", "Number of eval cases to generate (with --init)").option("--fail-level <number>", "Exit with error if pass rate (%) falls below threshold").action(withGlobalOpts(evalCommand));
15481
15657
  program.command("tui").description("Interactive terminal UI for generation, evaluation, and batch workflows").option("--repo <path>", "Repository path", process.cwd()).option("--no-animation", "Skip the animated banner intro").action(withGlobalOpts(tuiCommand));
15482
- program.command("instructions").description("Generate root and per-area AI instruction files").option("--repo <path>", "Repository path", process.cwd()).option("--output <path>", "Output path for copilot instructions").option("--model <name>", "Model for instructions generation", DEFAULT_MODEL).option("--force", "Overwrite existing area instruction files").option("--areas", "Also generate file-based instructions for detected areas").option("--areas-only", "Generate only file-based area instructions (skip root)").option("--area <name>", "Generate file-based instructions for a specific area").option("--strategy <mode>", "Instruction strategy (flat or nested)").option("--claude-md", "Generate CLAUDE.md files alongside AGENTS.md (nested strategy)").action(withGlobalOpts(instructionsCommand));
15483
- program.command("readiness").description("AI readiness assessment across 9 maturity pillars").argument("[path]", "Path to a local repository").option("--output <path>", "Write report to file (.json, .md, or .html)").option("--force", "Overwrite existing output file").option("--visual", "Generate visual HTML report").option("--per-area", "Show per-area readiness breakdown").option("--policy <sources>", "Policy sources (comma-separated: paths, npm packages)").option("--fail-level <number>", "Exit with error if readiness level is below threshold (1\u20135)").action(withGlobalOpts(readinessCommand));
15658
+ program.command("instructions").description("Generate instructions for the repository").option("--repo <path>", "Repository path", process.cwd()).option("--output <path>", "Output path for instructions").option("--model <name>", "Model for instructions generation", DEFAULT_MODEL).option("--force", "Overwrite existing area instruction files").option("--areas", "Also generate instructions for detected areas").option("--areas-only", "Generate only area instructions (skip root)").option("--area <name>", "Generate instructions for a specific area").option("--strategy <mode>", "Instruction strategy (flat or nested)").option("--claude-md", "Generate CLAUDE.md files alongside AGENTS.md (nested strategy)").option("--dry-run", "Preview generated files without writing anything").action(withGlobalOpts(instructionsCommand));
15659
+ program.command("readiness").description("Run readiness report across 9 maturity pillars").argument("[path]", "Path to a local repository").option("--output <path>", "Write report to file (.json, .md, or .html)").option("--force", "Overwrite existing output file").option("--visual", "Generate visual HTML report").option("--per-area", "Show per-area readiness breakdown").option("--policy <sources>", "Policy sources (comma-separated: paths, npm packages)").option("--fail-level <number>", "Exit with error if readiness level is below threshold (1\u20135)").action(withGlobalOpts(readinessCommand));
15484
15660
  program.command("batch").description("Batch process multiple repos across orgs").argument("[repos...]", "Repos in owner/name form (GitHub) or org/project/repo (Azure)").option("--output <path>", "Write results JSON to file").option("--provider <provider>", "Repo provider (github|azure)", "github").option("--model <name>", "Model for instructions generation", DEFAULT_MODEL).option("--branch <name>", "Branch name for PRs").action(withGlobalOpts(batchCommand));
15485
- program.command("batch-readiness").description("Generate batch AI readiness report for multiple repos").option("--output <path>", "Write HTML report to file").option("--policy <sources>", "Policy sources (comma-separated: paths, npm packages)").action(withGlobalOpts(batchReadinessCommand));
15661
+ program.command("batch-readiness").description("Run batch readiness report for multiple repos").option("--output <path>", "Write HTML report to file").option("--policy <sources>", "Policy sources (comma-separated: paths, npm packages)").action(withGlobalOpts(batchReadinessCommand));
15486
15662
  program.parse(argv);
15487
15663
  }
15488
15664