@microsoft/agentrc 2.0.1-8 → 2.0.1-9
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 +1 -0
- package/dist/index.js +280 -239
- package/dist/index.js.map +1 -1
- package/dist/skills/area-instructions/SKILL.md +37 -0
- package/dist/skills/nested-detail/SKILL.md +22 -0
- package/dist/skills/nested-hub/SKILL.md +44 -0
- package/dist/skills/root-instructions/SKILL.md +29 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3052,7 +3052,7 @@ var require_main = __commonJS({
|
|
|
3052
3052
|
exports.createMessageConnection = exports.createServerSocketTransport = exports.createClientSocketTransport = exports.createServerPipeTransport = exports.createClientPipeTransport = exports.generateRandomPipeName = exports.StreamMessageWriter = exports.StreamMessageReader = exports.SocketMessageWriter = exports.SocketMessageReader = exports.PortMessageWriter = exports.PortMessageReader = exports.IPCMessageWriter = exports.IPCMessageReader = void 0;
|
|
3053
3053
|
var ril_1 = require_ril();
|
|
3054
3054
|
ril_1.default.install();
|
|
3055
|
-
var
|
|
3055
|
+
var path23 = __require("path");
|
|
3056
3056
|
var os2 = __require("os");
|
|
3057
3057
|
var crypto_1 = __require("crypto");
|
|
3058
3058
|
var net_1 = __require("net");
|
|
@@ -3188,9 +3188,9 @@ var require_main = __commonJS({
|
|
|
3188
3188
|
}
|
|
3189
3189
|
let result;
|
|
3190
3190
|
if (XDG_RUNTIME_DIR) {
|
|
3191
|
-
result =
|
|
3191
|
+
result = path23.join(XDG_RUNTIME_DIR, `vscode-ipc-${randomSuffix}.sock`);
|
|
3192
3192
|
} else {
|
|
3193
|
-
result =
|
|
3193
|
+
result = path23.join(os2.tmpdir(), `vscode-${randomSuffix}.sock`);
|
|
3194
3194
|
}
|
|
3195
3195
|
const limit = safeIpcPathLengths.get(process.platform);
|
|
3196
3196
|
if (limit !== void 0 && result.length > limit) {
|
|
@@ -6674,7 +6674,7 @@ async function checkReposForInstructions(token, repos, onProgress) {
|
|
|
6674
6674
|
}
|
|
6675
6675
|
|
|
6676
6676
|
// packages/core/src/services/batch.ts
|
|
6677
|
-
import
|
|
6677
|
+
import path10 from "path";
|
|
6678
6678
|
|
|
6679
6679
|
// packages/core/src/utils/pr.ts
|
|
6680
6680
|
function buildInstructionsPrBody() {
|
|
@@ -6958,7 +6958,7 @@ async function checkReposForInstructions2(token, repos, onProgress) {
|
|
|
6958
6958
|
|
|
6959
6959
|
// packages/core/src/services/instructions.ts
|
|
6960
6960
|
import fs5 from "fs/promises";
|
|
6961
|
-
import
|
|
6961
|
+
import path7 from "path";
|
|
6962
6962
|
|
|
6963
6963
|
// packages/core/src/services/copilot.ts
|
|
6964
6964
|
import fs4 from "fs/promises";
|
|
@@ -7378,6 +7378,25 @@ async function createCopilotClient(cliConfig) {
|
|
|
7378
7378
|
}
|
|
7379
7379
|
}
|
|
7380
7380
|
|
|
7381
|
+
// packages/core/src/services/skills.ts
|
|
7382
|
+
import path6 from "path";
|
|
7383
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
7384
|
+
var overrideSkillsDir;
|
|
7385
|
+
function getBuiltinSkillsDir() {
|
|
7386
|
+
if (overrideSkillsDir) {
|
|
7387
|
+
return overrideSkillsDir;
|
|
7388
|
+
}
|
|
7389
|
+
const thisFile = fileURLToPath2(import.meta.url);
|
|
7390
|
+
const thisDir = path6.dirname(thisFile);
|
|
7391
|
+
if (thisDir.includes(path6.join("packages", "core", "src"))) {
|
|
7392
|
+
return path6.resolve(thisDir, "..", "..", "..", "..", "plugin", "skills");
|
|
7393
|
+
}
|
|
7394
|
+
return path6.resolve(thisDir, "skills");
|
|
7395
|
+
}
|
|
7396
|
+
function getSkillDirectory(_skillName) {
|
|
7397
|
+
return getBuiltinSkillsDir();
|
|
7398
|
+
}
|
|
7399
|
+
|
|
7381
7400
|
// packages/core/src/services/instructions.ts
|
|
7382
7401
|
async function detectExistingInstructions(repoPath, detailDirName = ".agents") {
|
|
7383
7402
|
const { agentsMdFiles, claudeMdFiles } = await findInstructionMarkerFiles(repoPath);
|
|
@@ -7401,7 +7420,7 @@ async function findInstructionMarkerFiles(repoPath) {
|
|
|
7401
7420
|
claudeMdFiles.push(relPath ? `${relPath}/${entry.name}` : entry.name);
|
|
7402
7421
|
}
|
|
7403
7422
|
} else if (entry.isDirectory()) {
|
|
7404
|
-
await walk(
|
|
7423
|
+
await walk(path7.join(dir, entry.name), relPath ? `${relPath}/${entry.name}` : entry.name);
|
|
7405
7424
|
}
|
|
7406
7425
|
}
|
|
7407
7426
|
}
|
|
@@ -7409,7 +7428,7 @@ async function findInstructionMarkerFiles(repoPath) {
|
|
|
7409
7428
|
return { agentsMdFiles: agentsMdFiles.sort(), claudeMdFiles: claudeMdFiles.sort() };
|
|
7410
7429
|
}
|
|
7411
7430
|
async function findModularInstructionFiles(repoPath) {
|
|
7412
|
-
const dir =
|
|
7431
|
+
const dir = path7.join(repoPath, ".github", "instructions");
|
|
7413
7432
|
const entries = await fs5.readdir(dir, { withFileTypes: true }).catch(() => []);
|
|
7414
7433
|
return entries.filter((e) => !e.isSymbolicLink() && e.isFile() && e.name.endsWith(".instructions.md")).map((e) => `.github/instructions/${e.name}`).sort();
|
|
7415
7434
|
}
|
|
@@ -7423,7 +7442,7 @@ async function findDetailFiles(repoPath, detailDirName) {
|
|
|
7423
7442
|
if (entry.isSymbolicLink()) continue;
|
|
7424
7443
|
if (entry.isDirectory()) {
|
|
7425
7444
|
if (entry.name === detailDirName) {
|
|
7426
|
-
const detailDir =
|
|
7445
|
+
const detailDir = path7.join(dir, entry.name);
|
|
7427
7446
|
const detailEntries = await fs5.readdir(detailDir, { withFileTypes: true }).catch(() => []);
|
|
7428
7447
|
for (const de of detailEntries) {
|
|
7429
7448
|
if (!de.isSymbolicLink() && de.isFile() && de.name.endsWith(".md")) {
|
|
@@ -7432,7 +7451,7 @@ async function findDetailFiles(repoPath, detailDirName) {
|
|
|
7432
7451
|
}
|
|
7433
7452
|
}
|
|
7434
7453
|
} else {
|
|
7435
|
-
await walk(
|
|
7454
|
+
await walk(path7.join(dir, entry.name), relPath ? `${relPath}/${entry.name}` : entry.name);
|
|
7436
7455
|
}
|
|
7437
7456
|
}
|
|
7438
7457
|
}
|
|
@@ -7514,11 +7533,11 @@ function isCopilotAuthError(error) {
|
|
|
7514
7533
|
return error instanceof Error && error.message.includes("Copilot CLI not logged in.");
|
|
7515
7534
|
}
|
|
7516
7535
|
function resolveAreaWorkingDirectory(repoPath, area) {
|
|
7517
|
-
const repoRoot =
|
|
7536
|
+
const repoRoot = path7.resolve(repoPath);
|
|
7518
7537
|
const rawWorkingDirectory = area?.workingDirectory ?? area?.path;
|
|
7519
7538
|
if (!rawWorkingDirectory) return repoRoot;
|
|
7520
|
-
const resolved = area?.workingDirectory ?
|
|
7521
|
-
if (resolved !== repoRoot && !resolved.startsWith(repoRoot +
|
|
7539
|
+
const resolved = area?.workingDirectory ? path7.resolve(repoRoot, rawWorkingDirectory) : path7.isAbsolute(rawWorkingDirectory) ? path7.resolve(rawWorkingDirectory) : path7.resolve(repoRoot, rawWorkingDirectory);
|
|
7540
|
+
if (resolved !== repoRoot && !resolved.startsWith(repoRoot + path7.sep)) {
|
|
7522
7541
|
throw new Error(`Invalid workingDirectory "${rawWorkingDirectory}": escapes repo boundary`);
|
|
7523
7542
|
}
|
|
7524
7543
|
return resolved;
|
|
@@ -7550,6 +7569,7 @@ async function generateCopilotInstructions(options) {
|
|
|
7550
7569
|
const preferredModel = options.model ?? DEFAULT_MODEL;
|
|
7551
7570
|
const systemContent = hasExistingInstructions ? "You are an expert codebase analyst. Your task is to generate a concise .github/copilot-instructions.md that complements existing instruction files. Use the available tools (glob, view, grep) 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 .github/copilot-instructions.md file. Use the available tools (glob, view, grep) to explore the codebase. When done, call the emit_file_content tool with the final markdown.";
|
|
7552
7571
|
const { tool: emitTool, getContent } = await createEmitTool();
|
|
7572
|
+
const rootSkillDir = getSkillDirectory("root-instructions");
|
|
7553
7573
|
const session = await client.createSession({
|
|
7554
7574
|
model: preferredModel,
|
|
7555
7575
|
streaming: true,
|
|
@@ -7560,7 +7580,8 @@ async function generateCopilotInstructions(options) {
|
|
|
7560
7580
|
tools: [emitTool],
|
|
7561
7581
|
excludedTools: INSTRUCTION_GENERATION_EXCLUDED_TOOLS,
|
|
7562
7582
|
onPermissionRequest: READ_ONLY_PERMISSION_HANDLER,
|
|
7563
|
-
infiniteSessions: { enabled: false }
|
|
7583
|
+
infiniteSessions: { enabled: false },
|
|
7584
|
+
skillDirectories: [rootSkillDir]
|
|
7564
7585
|
});
|
|
7565
7586
|
await trySetAutopilot(session);
|
|
7566
7587
|
let content = "";
|
|
@@ -7581,22 +7602,8 @@ async function generateCopilotInstructions(options) {
|
|
|
7581
7602
|
sessionError = getSessionError(errorMsg);
|
|
7582
7603
|
}
|
|
7583
7604
|
});
|
|
7584
|
-
const prompt =
|
|
7585
|
-
|
|
7586
|
-
Fan out multiple Explore subagents to map out the codebase in parallel:
|
|
7587
|
-
1. Check for existing instruction files: glob for **/{.github/copilot-instructions.md,AGENT.md,CLAUDE.md,.cursorrules,README.md}
|
|
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.
|
|
7589
|
-
3. Understand the structure: list key directories
|
|
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)
|
|
7591
|
-
|
|
7592
|
-
Generate concise instructions (~20-50 lines) covering:
|
|
7593
|
-
- Tech stack and architecture
|
|
7594
|
-
- Build/test commands
|
|
7595
|
-
- Project-specific conventions
|
|
7596
|
-
- Key files/directories
|
|
7597
|
-
- Monorepo structure and per-app layout (if this is a monorepo, describe the workspace organization, how apps relate to each other, and any shared libraries)
|
|
7598
|
-
${existingSection}
|
|
7599
|
-
When you have the complete markdown content, call the \`emit_file_content\` tool with it. Do NOT output the file content directly in chat.`;
|
|
7605
|
+
const prompt = `/root-instructions Analyze this codebase and generate a .github/copilot-instructions.md file.
|
|
7606
|
+
${existingSection}`;
|
|
7600
7607
|
progress("Analyzing codebase...");
|
|
7601
7608
|
let sendError;
|
|
7602
7609
|
try {
|
|
@@ -7633,6 +7640,7 @@ async function generateAreaInstructions(options) {
|
|
|
7633
7640
|
const preferredModel = options.model ?? DEFAULT_MODEL;
|
|
7634
7641
|
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.`;
|
|
7635
7642
|
const { tool: emitTool, getContent } = await createEmitTool();
|
|
7643
|
+
const areaSkillDir = getSkillDirectory("area-instructions");
|
|
7636
7644
|
const session = await client.createSession({
|
|
7637
7645
|
model: preferredModel,
|
|
7638
7646
|
streaming: true,
|
|
@@ -7643,7 +7651,8 @@ async function generateAreaInstructions(options) {
|
|
|
7643
7651
|
tools: [emitTool],
|
|
7644
7652
|
excludedTools: INSTRUCTION_GENERATION_EXCLUDED_TOOLS,
|
|
7645
7653
|
onPermissionRequest: READ_ONLY_PERMISSION_HANDLER,
|
|
7646
|
-
infiniteSessions: { enabled: false }
|
|
7654
|
+
infiniteSessions: { enabled: false },
|
|
7655
|
+
skillDirectories: [areaSkillDir]
|
|
7647
7656
|
});
|
|
7648
7657
|
await trySetAutopilot(session);
|
|
7649
7658
|
let content = "";
|
|
@@ -7664,30 +7673,13 @@ async function generateAreaInstructions(options) {
|
|
|
7664
7673
|
sessionError = getSessionError(errorMsg);
|
|
7665
7674
|
}
|
|
7666
7675
|
});
|
|
7667
|
-
const prompt =
|
|
7676
|
+
const prompt = `/area-instructions Analyze the "${area.name}" area of this codebase and generate an area instruction file.
|
|
7668
7677
|
|
|
7669
7678
|
This area covers files matching: ${applyToStr}
|
|
7670
7679
|
${area.description ? `Description: ${area.description}` : ""}
|
|
7671
|
-
|
|
7672
|
-
|
|
7673
|
-
|
|
7674
|
-
2. Identify the tech stack, dependencies, and frameworks used in this area
|
|
7675
|
-
3. Look at key source files to understand patterns and conventions specific to this area
|
|
7676
|
-
|
|
7677
|
-
Generate concise instructions (~10-30 lines) covering:
|
|
7678
|
-
- What this area does and its role in the overall project
|
|
7679
|
-
- Area-specific tech stack, dependencies, and frameworks
|
|
7680
|
-
- Coding conventions and patterns specific to this area
|
|
7681
|
-
- Build/test commands relevant to this area (if different from root)
|
|
7682
|
-
- Key files and directory structure within this area
|
|
7683
|
-
|
|
7684
|
-
IMPORTANT:
|
|
7685
|
-
- Focus ONLY on this specific area, not the whole repo
|
|
7686
|
-
- Do NOT repeat repo-wide information (that goes in the root copilot-instructions.md)
|
|
7687
|
-
- Keep it complementary to root instructions
|
|
7688
|
-
${existingSection ? `- Do NOT duplicate content already covered by existing instruction files
|
|
7689
|
-
${existingSection}` : ""}
|
|
7690
|
-
- When you have the complete markdown content, call the \`emit_file_content\` tool with it. Do NOT output the file content directly in chat.`;
|
|
7680
|
+
${existingSection ? `
|
|
7681
|
+
Do NOT duplicate content already covered by existing instruction files
|
|
7682
|
+
${existingSection}` : ""}`;
|
|
7691
7683
|
progress(`Analyzing area "${area.name}"...`);
|
|
7692
7684
|
let sendError;
|
|
7693
7685
|
try {
|
|
@@ -7724,7 +7716,7 @@ ${body}
|
|
|
7724
7716
|
`;
|
|
7725
7717
|
}
|
|
7726
7718
|
function areaInstructionPath(repoPath, area) {
|
|
7727
|
-
return
|
|
7719
|
+
return path7.join(
|
|
7728
7720
|
repoPath,
|
|
7729
7721
|
".github",
|
|
7730
7722
|
"instructions",
|
|
@@ -7734,7 +7726,7 @@ function areaInstructionPath(repoPath, area) {
|
|
|
7734
7726
|
async function writeAreaInstruction(repoPath, area, body, force) {
|
|
7735
7727
|
const filePath = areaInstructionPath(repoPath, area);
|
|
7736
7728
|
if (!body.trim()) return { status: "empty", filePath };
|
|
7737
|
-
await ensureDir(
|
|
7729
|
+
await ensureDir(path7.dirname(filePath));
|
|
7738
7730
|
const { wrote, reason } = await safeWriteFile(
|
|
7739
7731
|
filePath,
|
|
7740
7732
|
buildAreaInstructionContent(area, body),
|
|
@@ -7746,13 +7738,13 @@ async function writeAreaInstruction(repoPath, area, body, force) {
|
|
|
7746
7738
|
return { status: "written", filePath };
|
|
7747
7739
|
}
|
|
7748
7740
|
async function writeInstructionFile(repoPath, relativePath, content, force) {
|
|
7749
|
-
const resolvedRoot =
|
|
7750
|
-
const filePath =
|
|
7751
|
-
if (!filePath.startsWith(resolvedRoot +
|
|
7741
|
+
const resolvedRoot = path7.resolve(repoPath);
|
|
7742
|
+
const filePath = path7.resolve(repoPath, relativePath);
|
|
7743
|
+
if (!filePath.startsWith(resolvedRoot + path7.sep) && filePath !== resolvedRoot) {
|
|
7752
7744
|
throw new Error(`Invalid path: escapes repository root (${relativePath})`);
|
|
7753
7745
|
}
|
|
7754
7746
|
if (!content.trim()) return { status: "empty", filePath };
|
|
7755
|
-
await ensureDir(
|
|
7747
|
+
await ensureDir(path7.dirname(filePath));
|
|
7756
7748
|
const { wrote, reason } = await safeWriteFile(filePath, content, Boolean(force));
|
|
7757
7749
|
if (!wrote) {
|
|
7758
7750
|
return { status: reason === "symlink" ? "symlink" : "skipped", filePath };
|
|
@@ -7836,6 +7828,7 @@ async function generateNestedHub(client, options) {
|
|
|
7836
7828
|
const existingCtx = await detectExistingInstructions(options.repoPath, options.detailDir);
|
|
7837
7829
|
const existingSection = buildExistingInstructionsSection(existingCtx);
|
|
7838
7830
|
const { tool: emitTool, getContent } = await createEmitTool();
|
|
7831
|
+
const nestedSkillDir = getSkillDirectory("nested-hub");
|
|
7839
7832
|
const session = await client.createSession({
|
|
7840
7833
|
model,
|
|
7841
7834
|
streaming: true,
|
|
@@ -7846,7 +7839,8 @@ async function generateNestedHub(client, options) {
|
|
|
7846
7839
|
tools: [emitTool],
|
|
7847
7840
|
excludedTools: INSTRUCTION_GENERATION_EXCLUDED_TOOLS,
|
|
7848
7841
|
onPermissionRequest: READ_ONLY_PERMISSION_HANDLER,
|
|
7849
|
-
infiniteSessions: { enabled: false }
|
|
7842
|
+
infiniteSessions: { enabled: false },
|
|
7843
|
+
skillDirectories: [nestedSkillDir]
|
|
7850
7844
|
});
|
|
7851
7845
|
await trySetAutopilot(session);
|
|
7852
7846
|
let content = "";
|
|
@@ -7878,29 +7872,14 @@ ${options.childAreas.map((c) => `- ${c.name} (${c.path ?? "unknown path"})`).joi
|
|
|
7878
7872
|
Include a "## Sub-Projects" section with links to each child's AGENTS.md.` : "";
|
|
7879
7873
|
const parentContext = options.area?.parentArea ? `
|
|
7880
7874
|
This is a sub-project of "${options.area.parentArea}". Include a note linking back to the parent area.` : "";
|
|
7881
|
-
const prompt =
|
|
7882
|
-
|
|
7883
|
-
Use tools to explore the codebase structure, tech stack, and conventions.
|
|
7875
|
+
const prompt = `/nested-hub Generate a lean AGENTS.md hub file (~90-120 lines).${areaContext}${parentContext}
|
|
7884
7876
|
|
|
7885
|
-
|
|
7886
|
-
- Project overview and purpose
|
|
7887
|
-
- Key concepts and architecture
|
|
7888
|
-
- Coding conventions and guardrails
|
|
7889
|
-
- A "## Detailed Instructions" section listing links to detail files in \`${options.detailDir}/\`${childContext}
|
|
7877
|
+
Detail files go in \`${options.detailDir}/\`.${childContext}
|
|
7890
7878
|
|
|
7891
|
-
|
|
7892
|
-
|
|
7893
|
-
|
|
7894
|
-
|
|
7895
|
-
|
|
7896
|
-
Recommend 3-5 topics that would benefit from deep-dive detail files. Each slug becomes a filename: \`${options.detailDir}/{slug}.md\`.
|
|
7897
|
-
|
|
7898
|
-
IMPORTANT:
|
|
7899
|
-
- Keep the hub LEAN \u2014 overview and guardrails only, details go in separate files
|
|
7900
|
-
- The JSON block will be parsed and removed from the final output
|
|
7901
|
-
${existingSection ? `- Do NOT duplicate content from existing instruction files
|
|
7902
|
-
${existingSection}` : ""}
|
|
7903
|
-
- When you have the complete markdown content (including the trailing JSON topic block), call the \`emit_file_content\` tool with it. Do NOT output the content directly in chat.`;
|
|
7879
|
+
Recommend 3-5 topics for deep-dive detail files. Each slug becomes: \`${options.detailDir}/{slug}.md\`.
|
|
7880
|
+
${existingSection ? `
|
|
7881
|
+
Do NOT duplicate content from existing instruction files
|
|
7882
|
+
${existingSection}` : ""}`;
|
|
7904
7883
|
let sendError;
|
|
7905
7884
|
try {
|
|
7906
7885
|
await session.sendAndWait({ prompt }, 18e4);
|
|
@@ -7924,6 +7903,7 @@ async function generateNestedDetail(client, options) {
|
|
|
7924
7903
|
});
|
|
7925
7904
|
const model = options.model ?? DEFAULT_MODEL;
|
|
7926
7905
|
const { tool: emitTool, getContent } = await createEmitTool();
|
|
7906
|
+
const detailSkillDir = getSkillDirectory("nested-detail");
|
|
7927
7907
|
const session = await client.createSession({
|
|
7928
7908
|
model,
|
|
7929
7909
|
streaming: true,
|
|
@@ -7934,7 +7914,8 @@ async function generateNestedDetail(client, options) {
|
|
|
7934
7914
|
tools: [emitTool],
|
|
7935
7915
|
excludedTools: INSTRUCTION_GENERATION_EXCLUDED_TOOLS,
|
|
7936
7916
|
onPermissionRequest: READ_ONLY_PERMISSION_HANDLER,
|
|
7937
|
-
infiniteSessions: { enabled: false }
|
|
7917
|
+
infiniteSessions: { enabled: false },
|
|
7918
|
+
skillDirectories: [detailSkillDir]
|
|
7938
7919
|
});
|
|
7939
7920
|
await trySetAutopilot(session);
|
|
7940
7921
|
let content = "";
|
|
@@ -7956,22 +7937,11 @@ async function generateNestedDetail(client, options) {
|
|
|
7956
7937
|
}
|
|
7957
7938
|
});
|
|
7958
7939
|
const areaContext = options.area ? `Focus on the "${options.area.name}" area (files matching: ${Array.isArray(options.area.applyTo) ? options.area.applyTo.join(", ") : options.area.applyTo}).` : "Focus on the entire repository.";
|
|
7959
|
-
const prompt =
|
|
7940
|
+
const prompt = `/nested-detail Generate a deep-dive instruction file about "${options.topic.title}" for this codebase.
|
|
7960
7941
|
${areaContext}
|
|
7961
7942
|
|
|
7962
7943
|
Topic: ${options.topic.title}
|
|
7963
|
-
Description: ${options.topic.description}
|
|
7964
|
-
|
|
7965
|
-
Use tools to explore the codebase and understand the specific patterns, APIs, and conventions related to this topic.
|
|
7966
|
-
|
|
7967
|
-
The file should:
|
|
7968
|
-
- Start with \`# ${options.topic.title}\`
|
|
7969
|
-
- Include \`**When to read:** {one-line trigger condition}\` right after the heading
|
|
7970
|
-
- Cover ~50-100 lines of practical, actionable guidance
|
|
7971
|
-
- Include code patterns and examples found in the actual codebase
|
|
7972
|
-
- Be specific to this codebase, not generic advice
|
|
7973
|
-
|
|
7974
|
-
When you have the complete markdown content, call the \`emit_file_content\` tool with it. Do NOT output the content directly in chat.`;
|
|
7944
|
+
Description: ${options.topic.description}`;
|
|
7975
7945
|
let sendError;
|
|
7976
7946
|
try {
|
|
7977
7947
|
await session.sendAndWait({ prompt }, 18e4);
|
|
@@ -8006,7 +7976,7 @@ async function generateNestedInstructions(options) {
|
|
|
8006
7976
|
onProgress: options.onProgress
|
|
8007
7977
|
});
|
|
8008
7978
|
const basePath = options.area?.path ?? ".";
|
|
8009
|
-
const hubRelativePath =
|
|
7979
|
+
const hubRelativePath = path7.join(basePath, "AGENTS.md");
|
|
8010
7980
|
let finalHubContent = hubContent;
|
|
8011
7981
|
if (options.area) {
|
|
8012
7982
|
finalHubContent = `${buildAreaFrontmatter(options.area)}
|
|
@@ -8030,7 +8000,7 @@ ${hubContent}`;
|
|
|
8030
8000
|
});
|
|
8031
8001
|
if (detailContent) {
|
|
8032
8002
|
result.details.push({
|
|
8033
|
-
relativePath:
|
|
8003
|
+
relativePath: path7.join(basePath, options.detailDir, `${topic.slug}.md`),
|
|
8034
8004
|
content: detailContent,
|
|
8035
8005
|
topic: topic.title
|
|
8036
8006
|
});
|
|
@@ -8045,7 +8015,7 @@ ${hubContent}`;
|
|
|
8045
8015
|
}
|
|
8046
8016
|
if (options.claudeMd) {
|
|
8047
8017
|
result.claudeMd = {
|
|
8048
|
-
relativePath:
|
|
8018
|
+
relativePath: path7.join(basePath, "CLAUDE.md"),
|
|
8049
8019
|
content: "@AGENTS.md\n"
|
|
8050
8020
|
};
|
|
8051
8021
|
}
|
|
@@ -8068,11 +8038,11 @@ async function generateNestedAreaInstructions(options) {
|
|
|
8068
8038
|
|
|
8069
8039
|
// packages/core/src/services/readiness.ts
|
|
8070
8040
|
import fs7 from "fs/promises";
|
|
8071
|
-
import
|
|
8041
|
+
import path9 from "path";
|
|
8072
8042
|
|
|
8073
8043
|
// packages/core/src/services/policy.ts
|
|
8074
8044
|
import fs6 from "fs/promises";
|
|
8075
|
-
import
|
|
8045
|
+
import path8 from "path";
|
|
8076
8046
|
var DEFAULT_PASS_RATE = 0.8;
|
|
8077
8047
|
function validatePolicyConfig(obj, source, format = "module") {
|
|
8078
8048
|
if (typeof obj !== "object" || obj === null) {
|
|
@@ -8165,8 +8135,8 @@ function parsePolicySources(raw) {
|
|
|
8165
8135
|
}
|
|
8166
8136
|
async function loadPolicy(source, options) {
|
|
8167
8137
|
const jsonOnly = options?.jsonOnly ?? false;
|
|
8168
|
-
if (source.startsWith(".") ||
|
|
8169
|
-
const resolved =
|
|
8138
|
+
if (source.startsWith(".") || path8.isAbsolute(source)) {
|
|
8139
|
+
const resolved = path8.resolve(source);
|
|
8170
8140
|
if (resolved.endsWith(".json")) {
|
|
8171
8141
|
const data = await readJson(resolved);
|
|
8172
8142
|
if (!data) {
|
|
@@ -8797,7 +8767,7 @@ async function runReadinessReport(options) {
|
|
|
8797
8767
|
const repoPath = options.repoPath;
|
|
8798
8768
|
const analysis = await analyzeRepo(repoPath);
|
|
8799
8769
|
const rootFiles = await safeReadDir(repoPath);
|
|
8800
|
-
const rootPackageJson = await readJson(
|
|
8770
|
+
const rootPackageJson = await readJson(path9.join(repoPath, "package.json"));
|
|
8801
8771
|
const apps = analysis.apps?.length ? analysis.apps : [];
|
|
8802
8772
|
const context = {
|
|
8803
8773
|
repoPath,
|
|
@@ -9106,7 +9076,7 @@ function buildCriteria() {
|
|
|
9106
9076
|
impact: "medium",
|
|
9107
9077
|
effort: "low",
|
|
9108
9078
|
check: async (context) => {
|
|
9109
|
-
const found = await fileExists(
|
|
9079
|
+
const found = await fileExists(path9.join(context.repoPath, "CONTRIBUTING.md"));
|
|
9110
9080
|
return {
|
|
9111
9081
|
status: found ? "pass" : "fail",
|
|
9112
9082
|
reason: found ? void 0 : "Missing CONTRIBUTING.md for contributor workflows."
|
|
@@ -9207,7 +9177,7 @@ function buildCriteria() {
|
|
|
9207
9177
|
impact: "high",
|
|
9208
9178
|
effort: "low",
|
|
9209
9179
|
check: async (context) => {
|
|
9210
|
-
const found = await fileExists(
|
|
9180
|
+
const found = await fileExists(path9.join(context.repoPath, "SECURITY.md"));
|
|
9211
9181
|
return {
|
|
9212
9182
|
status: found ? "pass" : "fail",
|
|
9213
9183
|
reason: found ? void 0 : "Missing SECURITY.md policy."
|
|
@@ -9223,7 +9193,7 @@ function buildCriteria() {
|
|
|
9223
9193
|
impact: "medium",
|
|
9224
9194
|
effort: "medium",
|
|
9225
9195
|
check: async (context) => {
|
|
9226
|
-
const found = await fileExists(
|
|
9196
|
+
const found = await fileExists(path9.join(context.repoPath, ".github", "dependabot.yml"));
|
|
9227
9197
|
return {
|
|
9228
9198
|
status: found ? "pass" : "fail",
|
|
9229
9199
|
reason: found ? void 0 : "Missing .github/dependabot.yml configuration."
|
|
@@ -9429,7 +9399,7 @@ function buildCriteria() {
|
|
|
9429
9399
|
if (area?.scripts?.build) {
|
|
9430
9400
|
return { status: "pass" };
|
|
9431
9401
|
}
|
|
9432
|
-
const pkgPath =
|
|
9402
|
+
const pkgPath = path9.join(context.areaPath, "package.json");
|
|
9433
9403
|
const pkg = await readJson(pkgPath);
|
|
9434
9404
|
const scripts = pkg?.scripts ?? {};
|
|
9435
9405
|
const found = Boolean(scripts.build);
|
|
@@ -9454,7 +9424,7 @@ function buildCriteria() {
|
|
|
9454
9424
|
if (area?.scripts?.test) {
|
|
9455
9425
|
return { status: "pass" };
|
|
9456
9426
|
}
|
|
9457
|
-
const pkgPath =
|
|
9427
|
+
const pkgPath = path9.join(context.areaPath, "package.json");
|
|
9458
9428
|
const pkg = await readJson(pkgPath);
|
|
9459
9429
|
const scripts = pkg?.scripts ?? {};
|
|
9460
9430
|
const found = Boolean(scripts.test);
|
|
@@ -9477,7 +9447,7 @@ function buildCriteria() {
|
|
|
9477
9447
|
return { status: "skip", reason: "No area context." };
|
|
9478
9448
|
}
|
|
9479
9449
|
const sanitized = sanitizeAreaName(area.name);
|
|
9480
|
-
const instructionPath =
|
|
9450
|
+
const instructionPath = path9.join(
|
|
9481
9451
|
context.repoPath,
|
|
9482
9452
|
".github",
|
|
9483
9453
|
"instructions",
|
|
@@ -9498,7 +9468,7 @@ function buildExtras() {
|
|
|
9498
9468
|
id: "agents-doc",
|
|
9499
9469
|
title: "AGENTS.md present",
|
|
9500
9470
|
check: async (context) => {
|
|
9501
|
-
const found = await fileExists(
|
|
9471
|
+
const found = await fileExists(path9.join(context.repoPath, "AGENTS.md"));
|
|
9502
9472
|
return {
|
|
9503
9473
|
status: found ? "pass" : "fail",
|
|
9504
9474
|
reason: found ? void 0 : "Missing AGENTS.md to guide coding agents."
|
|
@@ -9663,11 +9633,11 @@ async function hasTypecheckConfig(repoPath) {
|
|
|
9663
9633
|
]);
|
|
9664
9634
|
}
|
|
9665
9635
|
async function hasGithubWorkflows(repoPath) {
|
|
9666
|
-
return fileExists(
|
|
9636
|
+
return fileExists(path9.join(repoPath, ".github", "workflows"));
|
|
9667
9637
|
}
|
|
9668
9638
|
async function hasCodeowners(repoPath) {
|
|
9669
|
-
const root = await fileExists(
|
|
9670
|
-
const github = await fileExists(
|
|
9639
|
+
const root = await fileExists(path9.join(repoPath, "CODEOWNERS"));
|
|
9640
|
+
const github = await fileExists(path9.join(repoPath, ".github", "CODEOWNERS"));
|
|
9671
9641
|
return root || github;
|
|
9672
9642
|
}
|
|
9673
9643
|
async function hasLicense(repoPath) {
|
|
@@ -9675,9 +9645,9 @@ async function hasLicense(repoPath) {
|
|
|
9675
9645
|
return files.some((file) => file.toLowerCase().startsWith("license"));
|
|
9676
9646
|
}
|
|
9677
9647
|
async function hasPullRequestTemplate(repoPath) {
|
|
9678
|
-
const direct = await fileExists(
|
|
9648
|
+
const direct = await fileExists(path9.join(repoPath, ".github", "PULL_REQUEST_TEMPLATE.md"));
|
|
9679
9649
|
if (direct) return true;
|
|
9680
|
-
const dir =
|
|
9650
|
+
const dir = path9.join(repoPath, ".github", "PULL_REQUEST_TEMPLATE");
|
|
9681
9651
|
try {
|
|
9682
9652
|
const entries = await fs7.readdir(dir);
|
|
9683
9653
|
return entries.some((entry) => entry.toLowerCase().endsWith(".md"));
|
|
@@ -9686,14 +9656,14 @@ async function hasPullRequestTemplate(repoPath) {
|
|
|
9686
9656
|
}
|
|
9687
9657
|
}
|
|
9688
9658
|
async function hasPrecommitConfig(repoPath) {
|
|
9689
|
-
const precommit = await fileExists(
|
|
9659
|
+
const precommit = await fileExists(path9.join(repoPath, ".pre-commit-config.yaml"));
|
|
9690
9660
|
if (precommit) return true;
|
|
9691
|
-
return fileExists(
|
|
9661
|
+
return fileExists(path9.join(repoPath, ".husky"));
|
|
9692
9662
|
}
|
|
9693
9663
|
async function hasArchitectureDoc(repoPath) {
|
|
9694
9664
|
const files = await safeReadDir(repoPath);
|
|
9695
9665
|
if (files.some((file) => file.toLowerCase() === "architecture.md")) return true;
|
|
9696
|
-
return fileExists(
|
|
9666
|
+
return fileExists(path9.join(repoPath, "docs", "architecture.md"));
|
|
9697
9667
|
}
|
|
9698
9668
|
async function hasCustomInstructions(repoPath) {
|
|
9699
9669
|
const found = [];
|
|
@@ -9710,14 +9680,14 @@ async function hasCustomInstructions(repoPath) {
|
|
|
9710
9680
|
"copilot-instructions.md"
|
|
9711
9681
|
];
|
|
9712
9682
|
for (const candidate of candidates) {
|
|
9713
|
-
if (await fileExists(
|
|
9683
|
+
if (await fileExists(path9.join(repoPath, candidate))) {
|
|
9714
9684
|
found.push(candidate);
|
|
9715
9685
|
}
|
|
9716
9686
|
}
|
|
9717
9687
|
return found;
|
|
9718
9688
|
}
|
|
9719
9689
|
async function hasFileBasedInstructions(repoPath) {
|
|
9720
|
-
const instructionsDir =
|
|
9690
|
+
const instructionsDir = path9.join(repoPath, ".github", "instructions");
|
|
9721
9691
|
try {
|
|
9722
9692
|
const entries = await fs7.readdir(instructionsDir);
|
|
9723
9693
|
return entries.filter((e) => e.endsWith(".instructions.md")).map((e) => `.github/instructions/${e}`);
|
|
@@ -9745,7 +9715,7 @@ async function checkInstructionConsistency(repoPath, foundFiles) {
|
|
|
9745
9715
|
}
|
|
9746
9716
|
const realPathMap = /* @__PURE__ */ new Map();
|
|
9747
9717
|
for (const file of foundFiles) {
|
|
9748
|
-
const fullPath =
|
|
9718
|
+
const fullPath = path9.join(repoPath, file);
|
|
9749
9719
|
try {
|
|
9750
9720
|
const real = await fs7.realpath(fullPath);
|
|
9751
9721
|
const group = realPathMap.get(real) ?? [];
|
|
@@ -9762,7 +9732,7 @@ async function checkInstructionConsistency(repoPath, foundFiles) {
|
|
|
9762
9732
|
const contents = [];
|
|
9763
9733
|
for (const group of groups) {
|
|
9764
9734
|
try {
|
|
9765
|
-
contents.push(await fs7.readFile(
|
|
9735
|
+
contents.push(await fs7.readFile(path9.join(repoPath, group[0]), "utf8"));
|
|
9766
9736
|
} catch {
|
|
9767
9737
|
contents.push("");
|
|
9768
9738
|
}
|
|
@@ -9781,17 +9751,17 @@ async function checkInstructionConsistency(repoPath, foundFiles) {
|
|
|
9781
9751
|
}
|
|
9782
9752
|
async function hasMcpConfig(repoPath) {
|
|
9783
9753
|
const found = [];
|
|
9784
|
-
if (await fileExists(
|
|
9754
|
+
if (await fileExists(path9.join(repoPath, ".vscode", "mcp.json"))) {
|
|
9785
9755
|
found.push(".vscode/mcp.json");
|
|
9786
9756
|
}
|
|
9787
|
-
if (await fileExists(
|
|
9757
|
+
if (await fileExists(path9.join(repoPath, "mcp.json"))) {
|
|
9788
9758
|
found.push("mcp.json");
|
|
9789
9759
|
}
|
|
9790
|
-
const settings = await readJson(
|
|
9760
|
+
const settings = await readJson(path9.join(repoPath, ".vscode", "settings.json"));
|
|
9791
9761
|
if (settings && (settings["mcp"] || settings["github.copilot.chat.mcp.enabled"])) {
|
|
9792
9762
|
found.push(".vscode/settings.json (mcp section)");
|
|
9793
9763
|
}
|
|
9794
|
-
if (await fileExists(
|
|
9764
|
+
if (await fileExists(path9.join(repoPath, ".claude", "mcp.json"))) {
|
|
9795
9765
|
found.push(".claude/mcp.json");
|
|
9796
9766
|
}
|
|
9797
9767
|
return found;
|
|
@@ -9800,13 +9770,13 @@ async function hasCustomAgents(repoPath) {
|
|
|
9800
9770
|
const found = [];
|
|
9801
9771
|
const agentDirs = [".github/agents", ".copilot/agents", ".github/copilot/agents"];
|
|
9802
9772
|
for (const dir of agentDirs) {
|
|
9803
|
-
if (await fileExists(
|
|
9773
|
+
if (await fileExists(path9.join(repoPath, dir))) {
|
|
9804
9774
|
found.push(dir);
|
|
9805
9775
|
}
|
|
9806
9776
|
}
|
|
9807
9777
|
const agentFiles = [".github/copilot-agents.yml", ".github/copilot-agents.yaml"];
|
|
9808
9778
|
for (const agentFile of agentFiles) {
|
|
9809
|
-
if (await fileExists(
|
|
9779
|
+
if (await fileExists(path9.join(repoPath, agentFile))) {
|
|
9810
9780
|
found.push(agentFile);
|
|
9811
9781
|
}
|
|
9812
9782
|
}
|
|
@@ -9821,7 +9791,7 @@ async function hasCopilotSkills(repoPath) {
|
|
|
9821
9791
|
".github/copilot/skills"
|
|
9822
9792
|
];
|
|
9823
9793
|
for (const dir of skillDirs) {
|
|
9824
|
-
if (await fileExists(
|
|
9794
|
+
if (await fileExists(path9.join(repoPath, dir))) {
|
|
9825
9795
|
found.push(dir);
|
|
9826
9796
|
}
|
|
9827
9797
|
}
|
|
@@ -9874,7 +9844,7 @@ async function processRepo(params) {
|
|
|
9874
9844
|
} = params;
|
|
9875
9845
|
try {
|
|
9876
9846
|
progress?.update(`${label}: Cloning...`);
|
|
9877
|
-
const cacheRoot =
|
|
9847
|
+
const cacheRoot = path10.join(process.cwd(), ".agentrc-cache");
|
|
9878
9848
|
const repoPath = validateCachePath(cacheRoot, ...cacheParts);
|
|
9879
9849
|
await ensureDir(repoPath);
|
|
9880
9850
|
if (!await isGitRepo(repoPath)) {
|
|
@@ -9900,8 +9870,8 @@ async function processRepo(params) {
|
|
|
9900
9870
|
if (!instructions.trim()) {
|
|
9901
9871
|
throw new Error("Generated instructions were empty");
|
|
9902
9872
|
}
|
|
9903
|
-
const instructionsPath =
|
|
9904
|
-
await ensureDir(
|
|
9873
|
+
const instructionsPath = path10.join(repoPath, ".github", "copilot-instructions.md");
|
|
9874
|
+
await ensureDir(path10.dirname(instructionsPath));
|
|
9905
9875
|
const { wrote, reason } = await safeWriteFile(instructionsPath, instructions, true);
|
|
9906
9876
|
if (!wrote) {
|
|
9907
9877
|
throw new Error(
|
|
@@ -10038,7 +10008,7 @@ async function processBatchReadinessRepo(options) {
|
|
|
10038
10008
|
try {
|
|
10039
10009
|
progress(`Cloning ${repo.fullName}...`);
|
|
10040
10010
|
const authedUrl = buildAuthedUrl(repo.cloneUrl, token, "github");
|
|
10041
|
-
await ensureDir(
|
|
10011
|
+
await ensureDir(path10.dirname(repoDir));
|
|
10042
10012
|
await cloneRepo(authedUrl, repoDir, { shallow: true });
|
|
10043
10013
|
await setRemoteUrl(repoDir, repo.cloneUrl).catch(() => {
|
|
10044
10014
|
});
|
|
@@ -11088,7 +11058,7 @@ import { render as render2 } from "ink";
|
|
|
11088
11058
|
// src/ui/BatchReadinessTui.tsx
|
|
11089
11059
|
import fs8 from "fs/promises";
|
|
11090
11060
|
import os from "os";
|
|
11091
|
-
import
|
|
11061
|
+
import path11 from "path";
|
|
11092
11062
|
|
|
11093
11063
|
// packages/core/src/services/visualReport.ts
|
|
11094
11064
|
function generateVisualReport(options) {
|
|
@@ -12114,7 +12084,7 @@ function BatchReadinessTui({ token, outputPath, policies }) {
|
|
|
12114
12084
|
setStatus("processing");
|
|
12115
12085
|
const selectedRepos = Array.from(selectedRepoIndices).map((i) => repos[i]);
|
|
12116
12086
|
const results2 = [];
|
|
12117
|
-
const tmpDir =
|
|
12087
|
+
const tmpDir = path11.join(os.tmpdir(), `agentrc-batch-readiness-${Date.now()}`);
|
|
12118
12088
|
try {
|
|
12119
12089
|
await ensureDir(tmpDir);
|
|
12120
12090
|
for (let i = 0; i < selectedRepos.length; i++) {
|
|
@@ -12151,7 +12121,7 @@ function BatchReadinessTui({ token, outputPath, policies }) {
|
|
|
12151
12121
|
title: "Batch Readiness Report",
|
|
12152
12122
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
12153
12123
|
});
|
|
12154
|
-
const finalOutputPath = outputPath ??
|
|
12124
|
+
const finalOutputPath = outputPath ?? path11.join(process.cwd(), "batch-readiness-report.html");
|
|
12155
12125
|
const { wrote, reason } = await safeWriteFile(finalOutputPath, html, true);
|
|
12156
12126
|
if (!wrote) throw new Error(reason === "symlink" ? "Path is a symlink" : "Write failed");
|
|
12157
12127
|
setStatus("complete");
|
|
@@ -12333,7 +12303,7 @@ async function batchReadinessCommand(options) {
|
|
|
12333
12303
|
}
|
|
12334
12304
|
|
|
12335
12305
|
// src/commands/eval.ts
|
|
12336
|
-
import
|
|
12306
|
+
import path13 from "path";
|
|
12337
12307
|
|
|
12338
12308
|
// packages/core/src/services/evalScaffold.ts
|
|
12339
12309
|
var EVAL_SCAFFOLD_TIMEOUT_MS = 6e5;
|
|
@@ -12516,17 +12486,17 @@ function normalizeEvalConfig(parsed, count) {
|
|
|
12516
12486
|
|
|
12517
12487
|
// packages/core/src/services/evaluator.ts
|
|
12518
12488
|
import fs9 from "fs/promises";
|
|
12519
|
-
import
|
|
12489
|
+
import path12 from "path";
|
|
12520
12490
|
var DEFAULT_SYSTEM_MESSAGE = "You are answering questions about this repository. Use tools to inspect the repo and cite its files. Avoid generic Copilot CLI details unless the prompt explicitly asks for them.";
|
|
12521
12491
|
async function runEval(options) {
|
|
12522
12492
|
const config = await loadConfig(options.configPath);
|
|
12523
12493
|
const instructionFile = config.instructionFile ?? ".github/copilot-instructions.md";
|
|
12524
|
-
const instructionPath =
|
|
12494
|
+
const instructionPath = path12.resolve(options.repoPath, instructionFile);
|
|
12525
12495
|
const instructionText = await readOptionalFile(instructionPath);
|
|
12526
12496
|
const baseSystemMessage = config.systemMessage ?? DEFAULT_SYSTEM_MESSAGE;
|
|
12527
12497
|
const progress = options.onProgress ?? (() => {
|
|
12528
12498
|
});
|
|
12529
|
-
const defaultOutputPath =
|
|
12499
|
+
const defaultOutputPath = path12.resolve(
|
|
12530
12500
|
options.repoPath,
|
|
12531
12501
|
".agentrc",
|
|
12532
12502
|
"evals",
|
|
@@ -12546,9 +12516,9 @@ async function runEval(options) {
|
|
|
12546
12516
|
const caseStartedAt = Date.now();
|
|
12547
12517
|
let caseWorkingDir;
|
|
12548
12518
|
if (testCase.workingDirectory) {
|
|
12549
|
-
const resolved =
|
|
12550
|
-
const root =
|
|
12551
|
-
if (resolved !== root && !resolved.startsWith(root +
|
|
12519
|
+
const resolved = path12.resolve(options.repoPath, testCase.workingDirectory);
|
|
12520
|
+
const root = path12.resolve(options.repoPath);
|
|
12521
|
+
if (resolved !== root && !resolved.startsWith(root + path12.sep)) {
|
|
12552
12522
|
throw new Error(
|
|
12553
12523
|
`Invalid workingDirectory "${testCase.workingDirectory}": escapes repo boundary`
|
|
12554
12524
|
);
|
|
@@ -12620,7 +12590,7 @@ async function runEval(options) {
|
|
|
12620
12590
|
};
|
|
12621
12591
|
let viewerPath;
|
|
12622
12592
|
if (outputPath) {
|
|
12623
|
-
await fs9.mkdir(
|
|
12593
|
+
await fs9.mkdir(path12.dirname(outputPath), { recursive: true });
|
|
12624
12594
|
await safeWriteFile(outputPath, JSON.stringify(output, null, 2), true);
|
|
12625
12595
|
viewerPath = buildViewerPath(outputPath);
|
|
12626
12596
|
await safeWriteFile(viewerPath, buildTrajectoryViewerHtml(output), true);
|
|
@@ -12934,7 +12904,7 @@ function formatTokenUsage(usage) {
|
|
|
12934
12904
|
function resolveOutputPath(repoPath, override, configValue) {
|
|
12935
12905
|
const chosen = override ?? configValue;
|
|
12936
12906
|
if (!chosen) return void 0;
|
|
12937
|
-
return
|
|
12907
|
+
return path12.isAbsolute(chosen) ? chosen : path12.resolve(repoPath, chosen);
|
|
12938
12908
|
}
|
|
12939
12909
|
function buildViewerPath(outputPath) {
|
|
12940
12910
|
if (outputPath.endsWith(".json")) {
|
|
@@ -13289,7 +13259,7 @@ function sanitizeValue(value, depth) {
|
|
|
13289
13259
|
|
|
13290
13260
|
// src/commands/eval.ts
|
|
13291
13261
|
async function evalCommand(configPathArg, options) {
|
|
13292
|
-
const repoPath =
|
|
13262
|
+
const repoPath = path13.resolve(options.repo ?? process.cwd());
|
|
13293
13263
|
if (options.listModels) {
|
|
13294
13264
|
try {
|
|
13295
13265
|
const models = await listCopilotModels();
|
|
@@ -13325,7 +13295,7 @@ async function evalCommand(configPathArg, options) {
|
|
|
13325
13295
|
return;
|
|
13326
13296
|
}
|
|
13327
13297
|
if (options.init) {
|
|
13328
|
-
const outputPath =
|
|
13298
|
+
const outputPath = path13.join(repoPath, "agentrc.eval.json");
|
|
13329
13299
|
const desiredCount = Math.max(1, Number.parseInt(options.count ?? "5", 10) || 5);
|
|
13330
13300
|
try {
|
|
13331
13301
|
const progress = createProgressReporter(!shouldLog(options));
|
|
@@ -13367,7 +13337,7 @@ async function evalCommand(configPathArg, options) {
|
|
|
13367
13337
|
}
|
|
13368
13338
|
return;
|
|
13369
13339
|
}
|
|
13370
|
-
const configPath =
|
|
13340
|
+
const configPath = path13.resolve(configPathArg ?? path13.join(repoPath, "agentrc.eval.json"));
|
|
13371
13341
|
try {
|
|
13372
13342
|
const progress = createProgressReporter(!shouldLog(options));
|
|
13373
13343
|
const { summary, results, viewerPath } = await runEval({
|
|
@@ -13414,30 +13384,30 @@ async function evalCommand(configPathArg, options) {
|
|
|
13414
13384
|
}
|
|
13415
13385
|
|
|
13416
13386
|
// src/commands/generate.ts
|
|
13417
|
-
import
|
|
13387
|
+
import path16 from "path";
|
|
13418
13388
|
|
|
13419
13389
|
// packages/core/src/services/generator.ts
|
|
13420
|
-
import
|
|
13390
|
+
import path14 from "path";
|
|
13421
13391
|
async function generateConfigs(options) {
|
|
13422
13392
|
const { repoPath, analysis, selections, force } = options;
|
|
13423
13393
|
const files = [];
|
|
13424
13394
|
if (selections.includes("mcp")) {
|
|
13425
|
-
const filePath =
|
|
13426
|
-
await ensureDir(
|
|
13395
|
+
const filePath = path14.join(repoPath, ".vscode", "mcp.json");
|
|
13396
|
+
await ensureDir(path14.dirname(filePath));
|
|
13427
13397
|
const content = renderMcp();
|
|
13428
13398
|
const { wrote } = await safeWriteFile(filePath, content, force);
|
|
13429
13399
|
files.push({
|
|
13430
|
-
path:
|
|
13400
|
+
path: path14.relative(process.cwd(), filePath),
|
|
13431
13401
|
action: wrote ? "wrote" : "skipped"
|
|
13432
13402
|
});
|
|
13433
13403
|
}
|
|
13434
13404
|
if (selections.includes("vscode")) {
|
|
13435
|
-
const filePath =
|
|
13436
|
-
await ensureDir(
|
|
13405
|
+
const filePath = path14.join(repoPath, ".vscode", "settings.json");
|
|
13406
|
+
await ensureDir(path14.dirname(filePath));
|
|
13437
13407
|
const content = renderVscodeSettings(analysis);
|
|
13438
13408
|
const { wrote } = await safeWriteFile(filePath, content, force);
|
|
13439
13409
|
files.push({
|
|
13440
|
-
path:
|
|
13410
|
+
path: path14.relative(process.cwd(), filePath),
|
|
13441
13411
|
action: wrote ? "wrote" : "skipped"
|
|
13442
13412
|
});
|
|
13443
13413
|
}
|
|
@@ -13488,16 +13458,16 @@ function renderVscodeSettings(analysis) {
|
|
|
13488
13458
|
}
|
|
13489
13459
|
|
|
13490
13460
|
// src/commands/instructions.ts
|
|
13491
|
-
import
|
|
13461
|
+
import path15 from "path";
|
|
13492
13462
|
function skipReason(action) {
|
|
13493
13463
|
if (action === "symlink") return "symlink";
|
|
13494
13464
|
if (action === "empty") return "empty content";
|
|
13495
13465
|
return "exists, use --force";
|
|
13496
13466
|
}
|
|
13497
13467
|
async function instructionsCommand(options) {
|
|
13498
|
-
const repoPath =
|
|
13499
|
-
const outputPath =
|
|
13500
|
-
options.output ??
|
|
13468
|
+
const repoPath = path15.resolve(options.repo ?? process.cwd());
|
|
13469
|
+
const outputPath = path15.resolve(
|
|
13470
|
+
options.output ?? path15.join(repoPath, ".github", "copilot-instructions.md")
|
|
13501
13471
|
);
|
|
13502
13472
|
const progress = createProgressReporter(!shouldLog(options));
|
|
13503
13473
|
const wantAreas = options.areas || options.areasOnly || options.area;
|
|
@@ -13544,7 +13514,7 @@ async function instructionsCommand(options) {
|
|
|
13544
13514
|
] : []
|
|
13545
13515
|
];
|
|
13546
13516
|
for (const file of dryFiles) {
|
|
13547
|
-
const relPath =
|
|
13517
|
+
const relPath = path15.relative(process.cwd(), path15.join(repoPath, file.path));
|
|
13548
13518
|
if (shouldLog(options)) {
|
|
13549
13519
|
progress.update(
|
|
13550
13520
|
`[dry-run] Would write ${relPath} (${Buffer.byteLength(file.content, "utf8")} bytes)`
|
|
@@ -13565,7 +13535,7 @@ async function instructionsCommand(options) {
|
|
|
13565
13535
|
} else {
|
|
13566
13536
|
const actions = await writeNestedInstructions(repoPath, nestedResult, options.force);
|
|
13567
13537
|
for (const action of actions) {
|
|
13568
|
-
const relPath =
|
|
13538
|
+
const relPath = path15.relative(process.cwd(), action.path);
|
|
13569
13539
|
if (action.action === "wrote") {
|
|
13570
13540
|
if (shouldLog(options)) progress.succeed(`Wrote ${relPath}`);
|
|
13571
13541
|
} else if (shouldLog(options)) {
|
|
@@ -13608,8 +13578,8 @@ async function instructionsCommand(options) {
|
|
|
13608
13578
|
}
|
|
13609
13579
|
if (content) {
|
|
13610
13580
|
if (options.dryRun) {
|
|
13611
|
-
const relPath =
|
|
13612
|
-
const displayPath =
|
|
13581
|
+
const relPath = path15.relative(repoPath, outputPath);
|
|
13582
|
+
const displayPath = path15.relative(process.cwd(), outputPath);
|
|
13613
13583
|
const byteCount = Buffer.byteLength(content, "utf8");
|
|
13614
13584
|
if (shouldLog(options)) {
|
|
13615
13585
|
progress.update(`[dry-run] Would write ${displayPath} (${byteCount} bytes)`);
|
|
@@ -13618,14 +13588,14 @@ async function instructionsCommand(options) {
|
|
|
13618
13588
|
dryRunFiles.push({ path: relPath, bytes: byteCount });
|
|
13619
13589
|
}
|
|
13620
13590
|
} else {
|
|
13621
|
-
await ensureDir(
|
|
13591
|
+
await ensureDir(path15.dirname(outputPath));
|
|
13622
13592
|
const { wrote, reason } = await safeWriteFile(
|
|
13623
13593
|
outputPath,
|
|
13624
13594
|
content,
|
|
13625
13595
|
Boolean(options.force)
|
|
13626
13596
|
);
|
|
13627
13597
|
if (!wrote) {
|
|
13628
|
-
const relPath =
|
|
13598
|
+
const relPath = path15.relative(process.cwd(), outputPath);
|
|
13629
13599
|
const why = reason === "symlink" ? "path is a symlink" : "file exists (use --force)";
|
|
13630
13600
|
if (options.json) {
|
|
13631
13601
|
const result = {
|
|
@@ -13647,7 +13617,7 @@ async function instructionsCommand(options) {
|
|
|
13647
13617
|
};
|
|
13648
13618
|
outputResult(result, true);
|
|
13649
13619
|
} else if (shouldLog(options)) {
|
|
13650
|
-
progress.succeed(`Updated ${
|
|
13620
|
+
progress.succeed(`Updated ${path15.relative(process.cwd(), outputPath)}`);
|
|
13651
13621
|
}
|
|
13652
13622
|
}
|
|
13653
13623
|
}
|
|
@@ -13714,7 +13684,7 @@ async function instructionsCommand(options) {
|
|
|
13714
13684
|
] : []
|
|
13715
13685
|
];
|
|
13716
13686
|
for (const file of dryFiles) {
|
|
13717
|
-
const relPath =
|
|
13687
|
+
const relPath = path15.relative(process.cwd(), path15.join(repoPath, file.path));
|
|
13718
13688
|
if (shouldLog(options)) {
|
|
13719
13689
|
progress.update(
|
|
13720
13690
|
`[dry-run] Would write ${relPath} (${Buffer.byteLength(file.content, "utf8")} bytes)`
|
|
@@ -13732,7 +13702,7 @@ async function instructionsCommand(options) {
|
|
|
13732
13702
|
} else {
|
|
13733
13703
|
const actions = await writeNestedInstructions(repoPath, nestedResult, options.force);
|
|
13734
13704
|
for (const action of actions) {
|
|
13735
|
-
const relPath =
|
|
13705
|
+
const relPath = path15.relative(process.cwd(), action.path);
|
|
13736
13706
|
if (action.action === "wrote") {
|
|
13737
13707
|
if (shouldLog(options)) progress.succeed(`Wrote ${relPath}`);
|
|
13738
13708
|
} else if (shouldLog(options)) {
|
|
@@ -13764,7 +13734,7 @@ async function instructionsCommand(options) {
|
|
|
13764
13734
|
}
|
|
13765
13735
|
if (options.json) {
|
|
13766
13736
|
dryRunFiles.push({
|
|
13767
|
-
path:
|
|
13737
|
+
path: path15.relative(repoPath, areaInstructionPath(repoPath, area)),
|
|
13768
13738
|
bytes: Buffer.byteLength(body, "utf8")
|
|
13769
13739
|
});
|
|
13770
13740
|
}
|
|
@@ -13785,7 +13755,7 @@ async function instructionsCommand(options) {
|
|
|
13785
13755
|
continue;
|
|
13786
13756
|
}
|
|
13787
13757
|
if (shouldLog(options)) {
|
|
13788
|
-
progress.succeed(`Wrote ${
|
|
13758
|
+
progress.succeed(`Wrote ${path15.relative(process.cwd(), result.filePath)}`);
|
|
13789
13759
|
}
|
|
13790
13760
|
}
|
|
13791
13761
|
}
|
|
@@ -13820,7 +13790,7 @@ async function instructionsCommand(options) {
|
|
|
13820
13790
|
|
|
13821
13791
|
// src/commands/generate.ts
|
|
13822
13792
|
async function generateCommand(type, repoPathArg, options) {
|
|
13823
|
-
const repoPath =
|
|
13793
|
+
const repoPath = path16.resolve(repoPathArg ?? process.cwd());
|
|
13824
13794
|
if (type === "instructions" || type === "agents") {
|
|
13825
13795
|
if (!options.quiet) {
|
|
13826
13796
|
process.stderr.write(
|
|
@@ -13834,7 +13804,7 @@ async function generateCommand(type, repoPathArg, options) {
|
|
|
13834
13804
|
`
|
|
13835
13805
|
);
|
|
13836
13806
|
}
|
|
13837
|
-
const output = type === "agents" ?
|
|
13807
|
+
const output = type === "agents" ? path16.join(repoPath, "AGENTS.md") : void 0;
|
|
13838
13808
|
await instructionsCommand({
|
|
13839
13809
|
repo: repoPath,
|
|
13840
13810
|
output,
|
|
@@ -13896,10 +13866,37 @@ async function generateCommand(type, repoPathArg, options) {
|
|
|
13896
13866
|
}
|
|
13897
13867
|
|
|
13898
13868
|
// src/commands/init.ts
|
|
13899
|
-
import
|
|
13869
|
+
import path18 from "path";
|
|
13870
|
+
|
|
13871
|
+
// packages/core/src/services/configScaffold.ts
|
|
13872
|
+
import path17 from "path";
|
|
13873
|
+
async function scaffoldAgentrcConfig(repoPath, areas, force = false) {
|
|
13874
|
+
if (areas.length === 0) return null;
|
|
13875
|
+
const configPath = path17.join(repoPath, "agentrc.config.json");
|
|
13876
|
+
const workspaces = await detectWorkspaces(repoPath, areas);
|
|
13877
|
+
const workspacePaths = workspaces.map((ws) => ws.path + "/");
|
|
13878
|
+
const standaloneAreas = areas.filter((a) => {
|
|
13879
|
+
if (!a.path) return true;
|
|
13880
|
+
const rel = path17.relative(repoPath, a.path).replace(/\\/gu, "/");
|
|
13881
|
+
return !workspacePaths.some((prefix) => rel.startsWith(prefix));
|
|
13882
|
+
}).map((a) => ({
|
|
13883
|
+
name: a.name,
|
|
13884
|
+
applyTo: a.applyTo,
|
|
13885
|
+
...a.description ? { description: a.description } : {}
|
|
13886
|
+
}));
|
|
13887
|
+
const agentrcConfig = {};
|
|
13888
|
+
if (workspaces.length > 0) agentrcConfig.workspaces = workspaces;
|
|
13889
|
+
if (standaloneAreas.length > 0) agentrcConfig.areas = standaloneAreas;
|
|
13890
|
+
if (!agentrcConfig.workspaces && !agentrcConfig.areas) return null;
|
|
13891
|
+
const configContent = JSON.stringify(agentrcConfig, null, 2) + "\n";
|
|
13892
|
+
const { wrote } = await safeWriteFile(configPath, configContent, force);
|
|
13893
|
+
return { wrote, configPath };
|
|
13894
|
+
}
|
|
13895
|
+
|
|
13896
|
+
// src/commands/init.ts
|
|
13900
13897
|
import { checkbox, select } from "@inquirer/prompts";
|
|
13901
13898
|
async function initCommand(repoPathArg, options) {
|
|
13902
|
-
let repoPath =
|
|
13899
|
+
let repoPath = path18.resolve(repoPathArg ?? process.cwd());
|
|
13903
13900
|
const provider = options.provider ?? (options.github ? "github" : void 0);
|
|
13904
13901
|
if (provider && provider !== "github" && provider !== "azure") {
|
|
13905
13902
|
outputError("Invalid provider. Use github or azure.", Boolean(options.json));
|
|
@@ -13937,7 +13934,7 @@ async function initCommand(repoPathArg, options) {
|
|
|
13937
13934
|
value: repo
|
|
13938
13935
|
}))
|
|
13939
13936
|
});
|
|
13940
|
-
const cacheRoot =
|
|
13937
|
+
const cacheRoot = path18.join(process.cwd(), ".agentrc-cache");
|
|
13941
13938
|
repoPath = validateCachePath(cacheRoot, selection.owner, selection.name);
|
|
13942
13939
|
await ensureDir(repoPath);
|
|
13943
13940
|
const hasGit = await isGitRepo(repoPath);
|
|
@@ -13990,7 +13987,7 @@ async function initCommand(repoPathArg, options) {
|
|
|
13990
13987
|
value: repo
|
|
13991
13988
|
}))
|
|
13992
13989
|
});
|
|
13993
|
-
const cacheRoot =
|
|
13990
|
+
const cacheRoot = path18.join(process.cwd(), ".agentrc-cache");
|
|
13994
13991
|
repoPath = validateCachePath(
|
|
13995
13992
|
cacheRoot,
|
|
13996
13993
|
orgSelection.name,
|
|
@@ -14029,17 +14026,17 @@ async function initCommand(repoPathArg, options) {
|
|
|
14029
14026
|
});
|
|
14030
14027
|
const allFiles = [];
|
|
14031
14028
|
if (selections.includes("instructions")) {
|
|
14032
|
-
const outputPath =
|
|
14033
|
-
await ensureDir(
|
|
14029
|
+
const outputPath = path18.join(repoPath, ".github", "copilot-instructions.md");
|
|
14030
|
+
await ensureDir(path18.dirname(outputPath));
|
|
14034
14031
|
try {
|
|
14035
14032
|
const content = await generateCopilotInstructions({ repoPath, model: options.model });
|
|
14036
14033
|
const { wrote } = await safeWriteFile(outputPath, content, Boolean(options.force));
|
|
14037
14034
|
allFiles.push({
|
|
14038
|
-
path:
|
|
14035
|
+
path: path18.relative(process.cwd(), outputPath),
|
|
14039
14036
|
action: wrote ? "wrote" : "skipped"
|
|
14040
14037
|
});
|
|
14041
14038
|
if (shouldLog(options)) {
|
|
14042
|
-
const rel =
|
|
14039
|
+
const rel = path18.relative(process.cwd(), outputPath);
|
|
14043
14040
|
process.stderr.write((wrote ? `Wrote ${rel}` : `Skipped ${rel} (exists)`) + "\n");
|
|
14044
14041
|
}
|
|
14045
14042
|
} catch (error) {
|
|
@@ -14067,28 +14064,12 @@ async function initCommand(repoPathArg, options) {
|
|
|
14067
14064
|
}
|
|
14068
14065
|
allFiles.push(...genResult.files);
|
|
14069
14066
|
if (analysis.areas && analysis.areas.length > 0) {
|
|
14070
|
-
const
|
|
14071
|
-
|
|
14072
|
-
|
|
14073
|
-
|
|
14074
|
-
if (!a.path) return true;
|
|
14075
|
-
const rel = path16.relative(repoPath, a.path).replace(/\\/gu, "/");
|
|
14076
|
-
return !workspacePaths.some((prefix) => rel.startsWith(prefix));
|
|
14077
|
-
}).map((a) => ({
|
|
14078
|
-
name: a.name,
|
|
14079
|
-
applyTo: a.applyTo,
|
|
14080
|
-
...a.description ? { description: a.description } : {}
|
|
14081
|
-
}));
|
|
14082
|
-
const agentrcConfig = {};
|
|
14083
|
-
if (workspaces.length > 0) agentrcConfig.workspaces = workspaces;
|
|
14084
|
-
if (standaloneAreas.length > 0) agentrcConfig.areas = standaloneAreas;
|
|
14085
|
-
if (agentrcConfig.workspaces || agentrcConfig.areas) {
|
|
14086
|
-
const configContent = JSON.stringify(agentrcConfig, null, 2) + "\n";
|
|
14087
|
-
const { wrote } = await safeWriteFile(configPath, configContent, Boolean(options.force));
|
|
14088
|
-
const rel = path16.relative(process.cwd(), configPath);
|
|
14089
|
-
allFiles.push({ path: rel, action: wrote ? "wrote" : "skipped" });
|
|
14067
|
+
const result = await scaffoldAgentrcConfig(repoPath, analysis.areas, Boolean(options.force));
|
|
14068
|
+
if (result) {
|
|
14069
|
+
const rel = path18.relative(process.cwd(), result.configPath);
|
|
14070
|
+
allFiles.push({ path: rel, action: result.wrote ? "wrote" : "skipped" });
|
|
14090
14071
|
if (shouldLog(options)) {
|
|
14091
|
-
process.stderr.write((wrote ? `Wrote ${rel}` : `Skipped ${rel} (exists)`) + "\n");
|
|
14072
|
+
process.stderr.write((result.wrote ? `Wrote ${rel}` : `Skipped ${rel} (exists)`) + "\n");
|
|
14092
14073
|
}
|
|
14093
14074
|
}
|
|
14094
14075
|
}
|
|
@@ -14115,7 +14096,7 @@ async function initCommand(repoPathArg, options) {
|
|
|
14115
14096
|
}
|
|
14116
14097
|
|
|
14117
14098
|
// src/commands/pr.ts
|
|
14118
|
-
import
|
|
14099
|
+
import path19 from "path";
|
|
14119
14100
|
var DEFAULT_PR_BRANCH = "agentrc/add-ai-config";
|
|
14120
14101
|
async function prCommand(repo, options) {
|
|
14121
14102
|
const provider = options.provider ?? "github";
|
|
@@ -14149,7 +14130,7 @@ async function prCommand(repo, options) {
|
|
|
14149
14130
|
try {
|
|
14150
14131
|
progress.update("Fetching repo info...");
|
|
14151
14132
|
const repoInfo = await getRepo(token2, organization, project, name2);
|
|
14152
|
-
const cacheRoot =
|
|
14133
|
+
const cacheRoot = path19.join(process.cwd(), ".agentrc-cache");
|
|
14153
14134
|
const repoPath = validateCachePath(cacheRoot, organization, project, name2);
|
|
14154
14135
|
await ensureDir(repoPath);
|
|
14155
14136
|
if (!await isGitRepo(repoPath)) {
|
|
@@ -14163,8 +14144,8 @@ async function prCommand(repo, options) {
|
|
|
14163
14144
|
await checkoutBranch(repoPath, branch);
|
|
14164
14145
|
progress.update("Generating instructions...");
|
|
14165
14146
|
const instructions = await generateCopilotInstructions({ repoPath, model: options.model });
|
|
14166
|
-
const instructionsPath =
|
|
14167
|
-
await ensureDir(
|
|
14147
|
+
const instructionsPath = path19.join(repoPath, ".github", "copilot-instructions.md");
|
|
14148
|
+
await ensureDir(path19.dirname(instructionsPath));
|
|
14168
14149
|
const { wrote, reason } = await safeWriteFile(instructionsPath, instructions, true);
|
|
14169
14150
|
if (!wrote) {
|
|
14170
14151
|
throw new Error(
|
|
@@ -14231,7 +14212,7 @@ async function prCommand(repo, options) {
|
|
|
14231
14212
|
try {
|
|
14232
14213
|
progress.update("Fetching repo info...");
|
|
14233
14214
|
const repoInfo = await getRepo2(token, owner, name);
|
|
14234
|
-
const cacheRoot =
|
|
14215
|
+
const cacheRoot = path19.join(process.cwd(), ".agentrc-cache");
|
|
14235
14216
|
const repoPath = validateCachePath(cacheRoot, owner, name);
|
|
14236
14217
|
await ensureDir(repoPath);
|
|
14237
14218
|
if (!await isGitRepo(repoPath)) {
|
|
@@ -14243,8 +14224,8 @@ async function prCommand(repo, options) {
|
|
|
14243
14224
|
await checkoutBranch(repoPath, branch);
|
|
14244
14225
|
progress.update("Generating instructions...");
|
|
14245
14226
|
const instructions = await generateCopilotInstructions({ repoPath, model: options.model });
|
|
14246
|
-
const instructionsPath =
|
|
14247
|
-
await ensureDir(
|
|
14227
|
+
const instructionsPath = path19.join(repoPath, ".github", "copilot-instructions.md");
|
|
14228
|
+
await ensureDir(path19.dirname(instructionsPath));
|
|
14248
14229
|
const { wrote, reason } = await safeWriteFile(instructionsPath, instructions, true);
|
|
14249
14230
|
if (!wrote) {
|
|
14250
14231
|
throw new Error(
|
|
@@ -14293,13 +14274,13 @@ async function prCommand(repo, options) {
|
|
|
14293
14274
|
}
|
|
14294
14275
|
|
|
14295
14276
|
// src/commands/readiness.ts
|
|
14296
|
-
import
|
|
14277
|
+
import path20 from "path";
|
|
14297
14278
|
import chalk3 from "chalk";
|
|
14298
14279
|
async function readinessCommand(repoPathArg, options) {
|
|
14299
|
-
const repoPath =
|
|
14300
|
-
const repoName =
|
|
14301
|
-
const resolvedOutputPath = options.output ?
|
|
14302
|
-
const outputExt = options.output ?
|
|
14280
|
+
const repoPath = path20.resolve(repoPathArg ?? process.cwd());
|
|
14281
|
+
const repoName = path20.basename(repoPath);
|
|
14282
|
+
const resolvedOutputPath = options.output ? path20.resolve(options.output) : "";
|
|
14283
|
+
const outputExt = options.output ? path20.extname(options.output).toLowerCase() : "";
|
|
14303
14284
|
let failLevelError;
|
|
14304
14285
|
let report;
|
|
14305
14286
|
try {
|
|
@@ -14361,7 +14342,7 @@ async function readinessCommand(repoPathArg, options) {
|
|
|
14361
14342
|
title: `Readiness Report: ${repoName}`,
|
|
14362
14343
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
14363
14344
|
});
|
|
14364
|
-
const outputPath = options.output ? resolvedOutputPath :
|
|
14345
|
+
const outputPath = options.output ? resolvedOutputPath : path20.join(repoPath, "readiness-report.html");
|
|
14365
14346
|
const { wrote, reason } = await safeWriteFile(outputPath, html, Boolean(options.force));
|
|
14366
14347
|
if (!wrote) {
|
|
14367
14348
|
const why = reason === "symlink" ? "path is a symlink" : "file exists (use --force)";
|
|
@@ -14596,26 +14577,32 @@ function formatReadinessMarkdown(report, repoName) {
|
|
|
14596
14577
|
}
|
|
14597
14578
|
|
|
14598
14579
|
// src/commands/tui.tsx
|
|
14599
|
-
import
|
|
14580
|
+
import path22 from "path";
|
|
14600
14581
|
import { render as render3 } from "ink";
|
|
14601
14582
|
|
|
14602
14583
|
// src/ui/tui.tsx
|
|
14603
14584
|
import fs10 from "fs/promises";
|
|
14604
|
-
import
|
|
14585
|
+
import path21 from "path";
|
|
14605
14586
|
import { Box as Box5, Text as Text5, useApp as useApp4, useInput as useInput4, useStdout, useIsScreenReaderEnabled as useIsScreenReaderEnabled5 } from "ink";
|
|
14606
14587
|
import { useEffect as useEffect5, useMemo, useState as useState5 } from "react";
|
|
14607
14588
|
import { Fragment, jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
14608
14589
|
var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
14609
14590
|
function useTerminalColumns() {
|
|
14610
14591
|
const { stdout } = useStdout();
|
|
14592
|
+
const accessible = useIsScreenReaderEnabled5();
|
|
14611
14593
|
const [columns, setColumns] = useState5(stdout.columns ?? 80);
|
|
14612
14594
|
useEffect5(() => {
|
|
14613
|
-
const onResize = () =>
|
|
14614
|
-
|
|
14595
|
+
const onResize = () => {
|
|
14596
|
+
if (!accessible) {
|
|
14597
|
+
stdout.write("\x1B[2J\x1B[H");
|
|
14598
|
+
}
|
|
14599
|
+
setColumns(stdout.columns ?? 80);
|
|
14600
|
+
};
|
|
14601
|
+
stdout.prependListener("resize", onResize);
|
|
14615
14602
|
return () => {
|
|
14616
14603
|
stdout.off("resize", onResize);
|
|
14617
14604
|
};
|
|
14618
|
-
}, [stdout]);
|
|
14605
|
+
}, [stdout, accessible]);
|
|
14619
14606
|
return columns;
|
|
14620
14607
|
}
|
|
14621
14608
|
function useSpinner(active) {
|
|
@@ -14700,6 +14687,7 @@ function AgentRCTui({ repoPath, skipAnimation = false }) {
|
|
|
14700
14687
|
const [modelPickTarget, setModelPickTarget] = useState5("eval");
|
|
14701
14688
|
const [modelCursor, setModelCursor] = useState5(0);
|
|
14702
14689
|
const [hasEvalConfig, setHasEvalConfig] = useState5(null);
|
|
14690
|
+
const [hasAgentrcConfig, setHasAgentrcConfig] = useState5(null);
|
|
14703
14691
|
const [activityLog, setActivityLog] = useState5([]);
|
|
14704
14692
|
const [generateTarget, setGenerateTarget] = useState5(
|
|
14705
14693
|
"copilot-instructions"
|
|
@@ -14709,7 +14697,7 @@ function AgentRCTui({ repoPath, skipAnimation = false }) {
|
|
|
14709
14697
|
const [isMonorepo, setIsMonorepo] = useState5(false);
|
|
14710
14698
|
const [repoAreas, setRepoAreas] = useState5([]);
|
|
14711
14699
|
const [areaCursor, setAreaCursor] = useState5(0);
|
|
14712
|
-
const repoLabel = useMemo(() =>
|
|
14700
|
+
const repoLabel = useMemo(() => path21.basename(repoPath), [repoPath]);
|
|
14713
14701
|
const repoFull = useMemo(() => repoPath, [repoPath]);
|
|
14714
14702
|
const isLoading = status === "generating" || status === "bootstrapping" || status === "evaluating" || status === "generating-areas" || status === "readiness-running";
|
|
14715
14703
|
const isMenu = status === "model-pick" || status === "eval-pick" || status === "batch-pick" || status === "generate-pick" || status === "generate-app-pick" || status === "generate-area-pick";
|
|
@@ -14721,14 +14709,24 @@ function AgentRCTui({ repoPath, skipAnimation = false }) {
|
|
|
14721
14709
|
setStatus("idle");
|
|
14722
14710
|
};
|
|
14723
14711
|
useEffect5(() => {
|
|
14724
|
-
const configPath =
|
|
14712
|
+
const configPath = path21.join(repoPath, "agentrc.eval.json");
|
|
14725
14713
|
fs10.access(configPath).then(() => setHasEvalConfig(true)).catch(() => setHasEvalConfig(false));
|
|
14714
|
+
const agentrcConfigCandidates = [
|
|
14715
|
+
path21.join(repoPath, "agentrc.config.json"),
|
|
14716
|
+
path21.join(repoPath, ".github", "agentrc.config.json")
|
|
14717
|
+
];
|
|
14718
|
+
Promise.all(
|
|
14719
|
+
agentrcConfigCandidates.map(
|
|
14720
|
+
(p) => fs10.access(p).then(() => true).catch(() => false)
|
|
14721
|
+
)
|
|
14722
|
+
).then((results) => setHasAgentrcConfig(results.some(Boolean))).catch(() => setHasAgentrcConfig(false));
|
|
14726
14723
|
analyzeRepo(repoPath).then((analysis) => {
|
|
14727
14724
|
const apps = analysis.apps ?? [];
|
|
14728
14725
|
setRepoApps(apps);
|
|
14729
14726
|
setIsMonorepo(analysis.isMonorepo ?? false);
|
|
14730
14727
|
setRepoAreas(analysis.areas ?? []);
|
|
14731
14728
|
}).catch((err) => {
|
|
14729
|
+
setRepoAreas([]);
|
|
14732
14730
|
addLog(`Repo analysis failed: ${err instanceof Error ? err.message : "unknown"}`, "error");
|
|
14733
14731
|
});
|
|
14734
14732
|
}, [repoPath]);
|
|
@@ -14782,7 +14780,7 @@ function AgentRCTui({ repoPath, skipAnimation = false }) {
|
|
|
14782
14780
|
};
|
|
14783
14781
|
useEffect5(() => {
|
|
14784
14782
|
let active = true;
|
|
14785
|
-
const configPath =
|
|
14783
|
+
const configPath = path21.join(repoPath, "agentrc.eval.json");
|
|
14786
14784
|
fs10.readFile(configPath, "utf8").then((raw) => {
|
|
14787
14785
|
if (!active) return;
|
|
14788
14786
|
const parsed = JSON.parse(raw);
|
|
@@ -14797,7 +14795,7 @@ function AgentRCTui({ repoPath, skipAnimation = false }) {
|
|
|
14797
14795
|
};
|
|
14798
14796
|
}, [repoPath]);
|
|
14799
14797
|
const bootstrapEvalConfig = async (count, force) => {
|
|
14800
|
-
const configPath =
|
|
14798
|
+
const configPath = path21.join(repoPath, "agentrc.eval.json");
|
|
14801
14799
|
try {
|
|
14802
14800
|
setStatus("bootstrapping");
|
|
14803
14801
|
setMessage("Generating eval cases with Copilot SDK...");
|
|
@@ -14836,13 +14834,13 @@ function AgentRCTui({ repoPath, skipAnimation = false }) {
|
|
|
14836
14834
|
if (status === "preview") {
|
|
14837
14835
|
if (input.toLowerCase() === "s") {
|
|
14838
14836
|
try {
|
|
14839
|
-
const outputPath = generateSavePath ||
|
|
14840
|
-
await fs10.mkdir(
|
|
14837
|
+
const outputPath = generateSavePath || path21.join(repoPath, ".github", "copilot-instructions.md");
|
|
14838
|
+
await fs10.mkdir(path21.dirname(outputPath), { recursive: true });
|
|
14841
14839
|
const { wrote, reason } = await safeWriteFile(outputPath, generatedContent, true);
|
|
14842
14840
|
if (!wrote)
|
|
14843
14841
|
throw new Error(reason === "symlink" ? "Path is a symlink" : "Write failed");
|
|
14844
14842
|
setStatus("done");
|
|
14845
|
-
const relPath =
|
|
14843
|
+
const relPath = path21.relative(repoPath, outputPath);
|
|
14846
14844
|
const msg = `Saved to ${relPath}`;
|
|
14847
14845
|
setMessage(msg);
|
|
14848
14846
|
addLog(msg, "success");
|
|
@@ -14876,7 +14874,7 @@ function AgentRCTui({ repoPath, skipAnimation = false }) {
|
|
|
14876
14874
|
setMessage("Enter a positive number of eval cases, then press Enter.");
|
|
14877
14875
|
return;
|
|
14878
14876
|
}
|
|
14879
|
-
const configPath =
|
|
14877
|
+
const configPath = path21.join(repoPath, "agentrc.eval.json");
|
|
14880
14878
|
setEvalBootstrapCount(count);
|
|
14881
14879
|
try {
|
|
14882
14880
|
await fs10.access(configPath);
|
|
@@ -14935,7 +14933,7 @@ function AgentRCTui({ repoPath, skipAnimation = false }) {
|
|
|
14935
14933
|
setStatus("generate-app-pick");
|
|
14936
14934
|
setMessage("Generate for root or per-app?");
|
|
14937
14935
|
} else {
|
|
14938
|
-
const savePath =
|
|
14936
|
+
const savePath = path21.join(repoPath, ".github", "copilot-instructions.md");
|
|
14939
14937
|
setGenerateSavePath(savePath);
|
|
14940
14938
|
await doGenerate(repoPath, savePath, "copilot-instructions");
|
|
14941
14939
|
}
|
|
@@ -14947,7 +14945,7 @@ function AgentRCTui({ repoPath, skipAnimation = false }) {
|
|
|
14947
14945
|
setStatus("generate-app-pick");
|
|
14948
14946
|
setMessage("Generate for root or per-app?");
|
|
14949
14947
|
} else {
|
|
14950
|
-
const savePath =
|
|
14948
|
+
const savePath = path21.join(repoPath, "AGENTS.md");
|
|
14951
14949
|
setGenerateSavePath(savePath);
|
|
14952
14950
|
await doGenerate(repoPath, savePath, "agents-md");
|
|
14953
14951
|
}
|
|
@@ -14972,7 +14970,7 @@ function AgentRCTui({ repoPath, skipAnimation = false }) {
|
|
|
14972
14970
|
}
|
|
14973
14971
|
if (status === "generate-app-pick") {
|
|
14974
14972
|
if (input.toLowerCase() === "r") {
|
|
14975
|
-
const savePath = generateTarget === "copilot-instructions" ?
|
|
14973
|
+
const savePath = generateTarget === "copilot-instructions" ? path21.join(repoPath, ".github", "copilot-instructions.md") : path21.join(repoPath, "AGENTS.md");
|
|
14976
14974
|
setGenerateSavePath(savePath);
|
|
14977
14975
|
await doGenerate(repoPath, savePath, generateTarget);
|
|
14978
14976
|
return;
|
|
@@ -14982,7 +14980,7 @@ function AgentRCTui({ repoPath, skipAnimation = false }) {
|
|
|
14982
14980
|
addLog(`Generating ${generateTarget} for ${repoApps.length} apps...`, "progress");
|
|
14983
14981
|
let count = 0;
|
|
14984
14982
|
for (const app2 of repoApps) {
|
|
14985
|
-
const savePath = generateTarget === "copilot-instructions" ?
|
|
14983
|
+
const savePath = generateTarget === "copilot-instructions" ? path21.join(app2.path, ".github", "copilot-instructions.md") : path21.join(app2.path, "AGENTS.md");
|
|
14986
14984
|
setMessage(`Generating for ${app2.name} (${count + 1}/${repoApps.length})...`);
|
|
14987
14985
|
try {
|
|
14988
14986
|
const content = await generateCopilotInstructions({
|
|
@@ -14990,11 +14988,11 @@ function AgentRCTui({ repoPath, skipAnimation = false }) {
|
|
|
14990
14988
|
onProgress: (msg) => setMessage(`${app2.name}: ${msg}`)
|
|
14991
14989
|
});
|
|
14992
14990
|
if (content.trim()) {
|
|
14993
|
-
await fs10.mkdir(
|
|
14991
|
+
await fs10.mkdir(path21.dirname(savePath), { recursive: true });
|
|
14994
14992
|
const { wrote: saved } = await safeWriteFile(savePath, content, true);
|
|
14995
14993
|
if (!saved) continue;
|
|
14996
14994
|
count++;
|
|
14997
|
-
addLog(`${app2.name}: saved ${
|
|
14995
|
+
addLog(`${app2.name}: saved ${path21.basename(savePath)}`, "success");
|
|
14998
14996
|
}
|
|
14999
14997
|
} catch (error) {
|
|
15000
14998
|
const msg = error instanceof Error ? error.message : "Failed.";
|
|
@@ -15008,7 +15006,7 @@ function AgentRCTui({ repoPath, skipAnimation = false }) {
|
|
|
15008
15006
|
const num = Number.parseInt(input, 10);
|
|
15009
15007
|
if (Number.isFinite(num) && num >= 1 && num <= repoApps.length) {
|
|
15010
15008
|
const app2 = repoApps[num - 1];
|
|
15011
|
-
const savePath = generateTarget === "copilot-instructions" ?
|
|
15009
|
+
const savePath = generateTarget === "copilot-instructions" ? path21.join(app2.path, ".github", "copilot-instructions.md") : path21.join(app2.path, "AGENTS.md");
|
|
15012
15010
|
setGenerateSavePath(savePath);
|
|
15013
15011
|
await doGenerate(app2.path, savePath, generateTarget);
|
|
15014
15012
|
return;
|
|
@@ -15036,7 +15034,7 @@ function AgentRCTui({ repoPath, skipAnimation = false }) {
|
|
|
15036
15034
|
const result = await writeAreaInstruction(repoPath, area, body);
|
|
15037
15035
|
if (result.status === "written") {
|
|
15038
15036
|
written++;
|
|
15039
|
-
addLog(`${area.name}: saved ${
|
|
15037
|
+
addLog(`${area.name}: saved ${path21.basename(result.filePath)}`, "success");
|
|
15040
15038
|
} else if (result.status === "skipped") {
|
|
15041
15039
|
addLog(`${area.name}: skipped (file exists)`, "info");
|
|
15042
15040
|
}
|
|
@@ -15130,8 +15128,8 @@ function AgentRCTui({ repoPath, skipAnimation = false }) {
|
|
|
15130
15128
|
}
|
|
15131
15129
|
if (status === "eval-pick") {
|
|
15132
15130
|
if (input.toLowerCase() === "r") {
|
|
15133
|
-
const configPath =
|
|
15134
|
-
const outputPath =
|
|
15131
|
+
const configPath = path21.join(repoPath, "agentrc.eval.json");
|
|
15132
|
+
const outputPath = path21.join(
|
|
15135
15133
|
repoPath,
|
|
15136
15134
|
".agentrc",
|
|
15137
15135
|
"evals",
|
|
@@ -15275,6 +15273,44 @@ function AgentRCTui({ repoPath, skipAnimation = false }) {
|
|
|
15275
15273
|
setMessage("Select eval action.");
|
|
15276
15274
|
return;
|
|
15277
15275
|
}
|
|
15276
|
+
if (input.toLowerCase() === "c") {
|
|
15277
|
+
if (hasAgentrcConfig) {
|
|
15278
|
+
setMessage("agentrc.config.json already exists.");
|
|
15279
|
+
return;
|
|
15280
|
+
}
|
|
15281
|
+
if (repoAreas.length === 0) {
|
|
15282
|
+
setMessage("No areas detected. Cannot scaffold agentrc.config.json.");
|
|
15283
|
+
return;
|
|
15284
|
+
}
|
|
15285
|
+
setStatus("generating");
|
|
15286
|
+
setMessage("Creating agentrc.config.json...");
|
|
15287
|
+
addLog("Scaffolding agentrc.config.json...", "progress");
|
|
15288
|
+
try {
|
|
15289
|
+
const result = await scaffoldAgentrcConfig(repoPath, repoAreas);
|
|
15290
|
+
if (!result) {
|
|
15291
|
+
setStatus("idle");
|
|
15292
|
+
setMessage("Nothing to scaffold \u2014 no areas or workspaces detected.");
|
|
15293
|
+
addLog("No areas to scaffold into config.", "info");
|
|
15294
|
+
} else if (result.wrote) {
|
|
15295
|
+
setHasAgentrcConfig(true);
|
|
15296
|
+
setStatus("done");
|
|
15297
|
+
const msg = `Created agentrc.config.json`;
|
|
15298
|
+
setMessage(msg);
|
|
15299
|
+
addLog(msg, "success");
|
|
15300
|
+
} else {
|
|
15301
|
+
setHasAgentrcConfig(true);
|
|
15302
|
+
setStatus("idle");
|
|
15303
|
+
setMessage("agentrc.config.json already exists (skipped).");
|
|
15304
|
+
addLog("agentrc.config.json already exists.", "info");
|
|
15305
|
+
}
|
|
15306
|
+
} catch (error) {
|
|
15307
|
+
setStatus("error");
|
|
15308
|
+
const msg = error instanceof Error ? error.message : "Failed to create config.";
|
|
15309
|
+
setMessage(msg);
|
|
15310
|
+
addLog(msg, "error");
|
|
15311
|
+
}
|
|
15312
|
+
return;
|
|
15313
|
+
}
|
|
15278
15314
|
if (input.toLowerCase() === "m") {
|
|
15279
15315
|
if (hideModelPicker) {
|
|
15280
15316
|
setMessage(
|
|
@@ -15389,6 +15425,10 @@ function AgentRCTui({ repoPath, skipAnimation = false }) {
|
|
|
15389
15425
|
/* @__PURE__ */ jsxs4(Text5, { children: [
|
|
15390
15426
|
/* @__PURE__ */ jsx7(Text5, { color: "gray", children: "Eval " }),
|
|
15391
15427
|
hasEvalConfig === null ? /* @__PURE__ */ jsx7(Text5, { color: "gray", dimColor: true, children: "checking..." }) : hasEvalConfig ? /* @__PURE__ */ jsx7(Text5, { color: "green", children: "agentrc.eval.json found" }) : /* @__PURE__ */ jsx7(Text5, { color: "yellow", children: "no eval config \u2014 press [I] to create" })
|
|
15428
|
+
] }),
|
|
15429
|
+
/* @__PURE__ */ jsxs4(Text5, { children: [
|
|
15430
|
+
/* @__PURE__ */ jsx7(Text5, { color: "gray", children: "Config " }),
|
|
15431
|
+
hasAgentrcConfig === null ? /* @__PURE__ */ jsx7(Text5, { color: "gray", dimColor: true, children: "checking..." }) : hasAgentrcConfig ? /* @__PURE__ */ jsx7(Text5, { color: "green", children: "agentrc.config.json found" }) : /* @__PURE__ */ jsx7(Text5, { color: "yellow", children: "no config \u2014 press [C] to create" })
|
|
15392
15432
|
] })
|
|
15393
15433
|
] }),
|
|
15394
15434
|
/* @__PURE__ */ jsx7(Divider, { columns: terminalColumns, label: "Activity", accessible }),
|
|
@@ -15464,7 +15504,7 @@ function AgentRCTui({ repoPath, skipAnimation = false }) {
|
|
|
15464
15504
|
/* @__PURE__ */ jsx7(Text5, { color: "white", children: app2.name }),
|
|
15465
15505
|
/* @__PURE__ */ jsxs4(Text5, { color: "gray", dimColor: true, children: [
|
|
15466
15506
|
" ",
|
|
15467
|
-
|
|
15507
|
+
path21.relative(repoPath, app2.path)
|
|
15468
15508
|
] })
|
|
15469
15509
|
] }, app2.name)) })
|
|
15470
15510
|
] }),
|
|
@@ -15498,7 +15538,7 @@ function AgentRCTui({ repoPath, skipAnimation = false }) {
|
|
|
15498
15538
|
children: [
|
|
15499
15539
|
/* @__PURE__ */ jsxs4(Text5, { color: "cyan", bold: true, children: [
|
|
15500
15540
|
"Preview (",
|
|
15501
|
-
|
|
15541
|
+
path21.relative(repoPath, generateSavePath) || generateTarget,
|
|
15502
15542
|
")"
|
|
15503
15543
|
] }),
|
|
15504
15544
|
/* @__PURE__ */ jsx7(Text5, { color: "gray", children: truncatedPreview })
|
|
@@ -15595,6 +15635,7 @@ function AgentRCTui({ repoPath, skipAnimation = false }) {
|
|
|
15595
15635
|
/* @__PURE__ */ jsxs4(Box5, { children: [
|
|
15596
15636
|
/* @__PURE__ */ jsx7(KeyHint, { k: "M", label: "Model" }),
|
|
15597
15637
|
/* @__PURE__ */ jsx7(KeyHint, { k: "J", label: "Judge" }),
|
|
15638
|
+
hasAgentrcConfig === false && /* @__PURE__ */ jsx7(KeyHint, { k: "C", label: "Create config" }),
|
|
15598
15639
|
/* @__PURE__ */ jsx7(KeyHint, { k: "Q", label: "Quit" })
|
|
15599
15640
|
] })
|
|
15600
15641
|
] }) })
|
|
@@ -15606,7 +15647,7 @@ function AgentRCTui({ repoPath, skipAnimation = false }) {
|
|
|
15606
15647
|
// src/commands/tui.tsx
|
|
15607
15648
|
import { jsx as jsx8 } from "react/jsx-runtime";
|
|
15608
15649
|
async function tuiCommand(options) {
|
|
15609
|
-
const repoPath =
|
|
15650
|
+
const repoPath = path22.resolve(options.repo ?? process.cwd());
|
|
15610
15651
|
const skipAnimation = options.animation === false;
|
|
15611
15652
|
try {
|
|
15612
15653
|
const accessible = options.accessible ? true : void 0;
|