@microsoft/agentrc 2.0.1-0 → 2.0.1-10

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;
@@ -3050,7 +3052,7 @@ var require_main = __commonJS({
3050
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;
3051
3053
  var ril_1 = require_ril();
3052
3054
  ril_1.default.install();
3053
- var path21 = __require("path");
3055
+ var path23 = __require("path");
3054
3056
  var os2 = __require("os");
3055
3057
  var crypto_1 = __require("crypto");
3056
3058
  var net_1 = __require("net");
@@ -3186,9 +3188,9 @@ var require_main = __commonJS({
3186
3188
  }
3187
3189
  let result;
3188
3190
  if (XDG_RUNTIME_DIR) {
3189
- result = path21.join(XDG_RUNTIME_DIR, `vscode-ipc-${randomSuffix}.sock`);
3191
+ result = path23.join(XDG_RUNTIME_DIR, `vscode-ipc-${randomSuffix}.sock`);
3190
3192
  } else {
3191
- result = path21.join(os2.tmpdir(), `vscode-${randomSuffix}.sock`);
3193
+ result = path23.join(os2.tmpdir(), `vscode-${randomSuffix}.sock`);
3192
3194
  }
3193
3195
  const limit = safeIpcPathLengths.get(process.platform);
3194
3196
  if (limit !== void 0 && result.length > limit) {
@@ -3329,23 +3331,63 @@ function createSessionRpc(connection, sessionId) {
3329
3331
  readFile: async (params) => connection.sendRequest("session.workspace.readFile", { sessionId, ...params }),
3330
3332
  createFile: async (params) => connection.sendRequest("session.workspace.createFile", { sessionId, ...params })
3331
3333
  },
3334
+ /** @experimental */
3332
3335
  fleet: {
3333
3336
  start: async (params) => connection.sendRequest("session.fleet.start", { sessionId, ...params })
3334
3337
  },
3338
+ /** @experimental */
3335
3339
  agent: {
3336
3340
  list: async () => connection.sendRequest("session.agent.list", { sessionId }),
3337
3341
  getCurrent: async () => connection.sendRequest("session.agent.getCurrent", { sessionId }),
3338
3342
  select: async (params) => connection.sendRequest("session.agent.select", { sessionId, ...params }),
3339
- deselect: async () => connection.sendRequest("session.agent.deselect", { sessionId })
3343
+ deselect: async () => connection.sendRequest("session.agent.deselect", { sessionId }),
3344
+ reload: async () => connection.sendRequest("session.agent.reload", { sessionId })
3340
3345
  },
3346
+ /** @experimental */
3347
+ skills: {
3348
+ list: async () => connection.sendRequest("session.skills.list", { sessionId }),
3349
+ enable: async (params) => connection.sendRequest("session.skills.enable", { sessionId, ...params }),
3350
+ disable: async (params) => connection.sendRequest("session.skills.disable", { sessionId, ...params }),
3351
+ reload: async () => connection.sendRequest("session.skills.reload", { sessionId })
3352
+ },
3353
+ /** @experimental */
3354
+ mcp: {
3355
+ list: async () => connection.sendRequest("session.mcp.list", { sessionId }),
3356
+ enable: async (params) => connection.sendRequest("session.mcp.enable", { sessionId, ...params }),
3357
+ disable: async (params) => connection.sendRequest("session.mcp.disable", { sessionId, ...params }),
3358
+ reload: async () => connection.sendRequest("session.mcp.reload", { sessionId })
3359
+ },
3360
+ /** @experimental */
3361
+ plugins: {
3362
+ list: async () => connection.sendRequest("session.plugins.list", { sessionId })
3363
+ },
3364
+ /** @experimental */
3365
+ extensions: {
3366
+ list: async () => connection.sendRequest("session.extensions.list", { sessionId }),
3367
+ enable: async (params) => connection.sendRequest("session.extensions.enable", { sessionId, ...params }),
3368
+ disable: async (params) => connection.sendRequest("session.extensions.disable", { sessionId, ...params }),
3369
+ reload: async () => connection.sendRequest("session.extensions.reload", { sessionId })
3370
+ },
3371
+ /** @experimental */
3341
3372
  compaction: {
3342
3373
  compact: async () => connection.sendRequest("session.compaction.compact", { sessionId })
3343
3374
  },
3344
3375
  tools: {
3345
3376
  handlePendingToolCall: async (params) => connection.sendRequest("session.tools.handlePendingToolCall", { sessionId, ...params })
3346
3377
  },
3378
+ commands: {
3379
+ handlePendingCommand: async (params) => connection.sendRequest("session.commands.handlePendingCommand", { sessionId, ...params })
3380
+ },
3381
+ ui: {
3382
+ elicitation: async (params) => connection.sendRequest("session.ui.elicitation", { sessionId, ...params })
3383
+ },
3347
3384
  permissions: {
3348
3385
  handlePendingPermissionRequest: async (params) => connection.sendRequest("session.permissions.handlePendingPermissionRequest", { sessionId, ...params })
3386
+ },
3387
+ log: async (params) => connection.sendRequest("session.log", { sessionId, ...params }),
3388
+ shell: {
3389
+ exec: async (params) => connection.sendRequest("session.shell.exec", { sessionId, ...params }),
3390
+ kill: async (params) => connection.sendRequest("session.shell.kill", { sessionId, ...params })
3349
3391
  }
3350
3392
  };
3351
3393
  }
@@ -3367,13 +3409,30 @@ var init_sdkProtocolVersion = __esm({
3367
3409
  }
3368
3410
  });
3369
3411
 
3412
+ // node_modules/@github/copilot-sdk/dist/telemetry.js
3413
+ async function getTraceContext(provider) {
3414
+ if (!provider) return {};
3415
+ try {
3416
+ return await provider() ?? {};
3417
+ } catch {
3418
+ return {};
3419
+ }
3420
+ }
3421
+ var init_telemetry = __esm({
3422
+ "node_modules/@github/copilot-sdk/dist/telemetry.js"() {
3423
+ "use strict";
3424
+ }
3425
+ });
3426
+
3370
3427
  // node_modules/@github/copilot-sdk/dist/session.js
3371
- var import_node, CopilotSession;
3428
+ var import_node, NO_RESULT_PERMISSION_V2_ERROR, CopilotSession;
3372
3429
  var init_session = __esm({
3373
3430
  "node_modules/@github/copilot-sdk/dist/session.js"() {
3374
3431
  "use strict";
3375
3432
  import_node = __toESM(require_node(), 1);
3376
3433
  init_rpc();
3434
+ init_telemetry();
3435
+ NO_RESULT_PERMISSION_V2_ERROR = "Permission handlers cannot return 'no-result' when connected to a protocol v2 server.";
3377
3436
  CopilotSession = class {
3378
3437
  /**
3379
3438
  * Creates a new CopilotSession instance.
@@ -3381,12 +3440,14 @@ var init_session = __esm({
3381
3440
  * @param sessionId - The unique identifier for this session
3382
3441
  * @param connection - The JSON-RPC message connection to the Copilot CLI
3383
3442
  * @param workspacePath - Path to the session workspace directory (when infinite sessions enabled)
3443
+ * @param traceContextProvider - Optional callback to get W3C Trace Context for outbound RPCs
3384
3444
  * @internal This constructor is internal. Use {@link CopilotClient.createSession} to create sessions.
3385
3445
  */
3386
- constructor(sessionId, connection, _workspacePath) {
3446
+ constructor(sessionId, connection, _workspacePath, traceContextProvider) {
3387
3447
  this.sessionId = sessionId;
3388
3448
  this.connection = connection;
3389
3449
  this._workspacePath = _workspacePath;
3450
+ this.traceContextProvider = traceContextProvider;
3390
3451
  }
3391
3452
  eventHandlers = /* @__PURE__ */ new Set();
3392
3453
  typedEventHandlers = /* @__PURE__ */ new Map();
@@ -3394,7 +3455,9 @@ var init_session = __esm({
3394
3455
  permissionHandler;
3395
3456
  userInputHandler;
3396
3457
  hooks;
3458
+ transformCallbacks;
3397
3459
  _rpc = null;
3460
+ traceContextProvider;
3398
3461
  /**
3399
3462
  * Typed session-scoped RPC methods.
3400
3463
  */
@@ -3432,6 +3495,7 @@ var init_session = __esm({
3432
3495
  */
3433
3496
  async send(options) {
3434
3497
  const response = await this.connection.sendRequest("session.send", {
3498
+ ...await getTraceContext(this.traceContextProvider),
3435
3499
  sessionId: this.sessionId,
3436
3500
  prompt: options.prompt,
3437
3501
  attachments: options.attachments,
@@ -3561,9 +3625,19 @@ var init_session = __esm({
3561
3625
  const { requestId, toolName } = event.data;
3562
3626
  const args2 = event.data.arguments;
3563
3627
  const toolCallId = event.data.toolCallId;
3628
+ const traceparent = event.data.traceparent;
3629
+ const tracestate = event.data.tracestate;
3564
3630
  const handler = this.toolHandlers.get(toolName);
3565
3631
  if (handler) {
3566
- void this._executeToolAndRespond(requestId, toolName, toolCallId, args2, handler);
3632
+ void this._executeToolAndRespond(
3633
+ requestId,
3634
+ toolName,
3635
+ toolCallId,
3636
+ args2,
3637
+ handler,
3638
+ traceparent,
3639
+ tracestate
3640
+ );
3567
3641
  }
3568
3642
  } else if (event.type === "permission.requested") {
3569
3643
  const { requestId, permissionRequest } = event.data;
@@ -3576,13 +3650,15 @@ var init_session = __esm({
3576
3650
  * Executes a tool handler and sends the result back via RPC.
3577
3651
  * @internal
3578
3652
  */
3579
- async _executeToolAndRespond(requestId, toolName, toolCallId, args2, handler) {
3653
+ async _executeToolAndRespond(requestId, toolName, toolCallId, args2, handler, traceparent, tracestate) {
3580
3654
  try {
3581
3655
  const rawResult = await handler(args2, {
3582
3656
  sessionId: this.sessionId,
3583
3657
  toolCallId,
3584
3658
  toolName,
3585
- arguments: args2
3659
+ arguments: args2,
3660
+ traceparent,
3661
+ tracestate
3586
3662
  });
3587
3663
  let result;
3588
3664
  if (rawResult == null) {
@@ -3613,6 +3689,9 @@ var init_session = __esm({
3613
3689
  const result = await this.permissionHandler(permissionRequest, {
3614
3690
  sessionId: this.sessionId
3615
3691
  });
3692
+ if (result.kind === "no-result") {
3693
+ return;
3694
+ }
3616
3695
  await this.rpc.permissions.handlePendingPermissionRequest({ requestId, result });
3617
3696
  } catch (_error) {
3618
3697
  try {
@@ -3693,6 +3772,40 @@ var init_session = __esm({
3693
3772
  registerHooks(hooks) {
3694
3773
  this.hooks = hooks;
3695
3774
  }
3775
+ /**
3776
+ * Registers transform callbacks for system message sections.
3777
+ *
3778
+ * @param callbacks - Map of section ID to transform callback, or undefined to clear
3779
+ * @internal This method is typically called internally when creating a session.
3780
+ */
3781
+ registerTransformCallbacks(callbacks) {
3782
+ this.transformCallbacks = callbacks;
3783
+ }
3784
+ /**
3785
+ * Handles a systemMessage.transform request from the runtime.
3786
+ * Dispatches each section to its registered transform callback.
3787
+ *
3788
+ * @param sections - Map of section IDs to their current rendered content
3789
+ * @returns A promise that resolves with the transformed sections
3790
+ * @internal This method is for internal use by the SDK.
3791
+ */
3792
+ async _handleSystemMessageTransform(sections) {
3793
+ const result = {};
3794
+ for (const [sectionId, { content }] of Object.entries(sections)) {
3795
+ const callback = this.transformCallbacks?.get(sectionId);
3796
+ if (callback) {
3797
+ try {
3798
+ const transformed = await callback(content);
3799
+ result[sectionId] = { content: transformed };
3800
+ } catch (_error) {
3801
+ result[sectionId] = { content };
3802
+ }
3803
+ } else {
3804
+ result[sectionId] = { content };
3805
+ }
3806
+ }
3807
+ return { sections: result };
3808
+ }
3696
3809
  /**
3697
3810
  * Handles a permission request in the v2 protocol format (synchronous RPC).
3698
3811
  * Used as a back-compat adapter when connected to a v2 server.
@@ -3709,8 +3822,14 @@ var init_session = __esm({
3709
3822
  const result = await this.permissionHandler(request, {
3710
3823
  sessionId: this.sessionId
3711
3824
  });
3825
+ if (result.kind === "no-result") {
3826
+ throw new Error(NO_RESULT_PERMISSION_V2_ERROR);
3827
+ }
3712
3828
  return result;
3713
- } catch (_error) {
3829
+ } catch (error) {
3830
+ if (error instanceof Error && error.message === NO_RESULT_PERMISSION_V2_ERROR) {
3831
+ throw error;
3832
+ }
3714
3833
  return { kind: "denied-no-approval-rule-and-could-not-request-from-user" };
3715
3834
  }
3716
3835
  }
@@ -3866,14 +3985,35 @@ var init_session = __esm({
3866
3985
  * The new model takes effect for the next message. Conversation history is preserved.
3867
3986
  *
3868
3987
  * @param model - Model ID to switch to
3988
+ * @param options - Optional settings for the new model
3869
3989
  *
3870
3990
  * @example
3871
3991
  * ```typescript
3872
3992
  * await session.setModel("gpt-4.1");
3993
+ * await session.setModel("claude-sonnet-4.6", { reasoningEffort: "high" });
3873
3994
  * ```
3874
3995
  */
3875
- async setModel(model) {
3876
- await this.rpc.model.switchTo({ modelId: model });
3996
+ async setModel(model, options) {
3997
+ await this.rpc.model.switchTo({ modelId: model, ...options });
3998
+ }
3999
+ /**
4000
+ * Log a message to the session timeline.
4001
+ * The message appears in the session event stream and is visible to SDK consumers
4002
+ * and (for non-ephemeral messages) persisted to the session event log on disk.
4003
+ *
4004
+ * @param message - Human-readable message text
4005
+ * @param options - Optional log level and ephemeral flag
4006
+ *
4007
+ * @example
4008
+ * ```typescript
4009
+ * await session.log("Processing started");
4010
+ * await session.log("Disk usage high", { level: "warning" });
4011
+ * await session.log("Connection failed", { level: "error" });
4012
+ * await session.log("Debug info", { ephemeral: true });
4013
+ * ```
4014
+ */
4015
+ async log(message, options) {
4016
+ await this.rpc.log({ message, ...options });
3877
4017
  }
3878
4018
  };
3879
4019
  }
@@ -3881,7 +4021,9 @@ var init_session = __esm({
3881
4021
 
3882
4022
  // node_modules/@github/copilot-sdk/dist/client.js
3883
4023
  import { spawn } from "child_process";
4024
+ import { randomUUID } from "crypto";
3884
4025
  import { existsSync } from "fs";
4026
+ import { createRequire } from "module";
3885
4027
  import { Socket } from "net";
3886
4028
  import { dirname, join } from "path";
3887
4029
  import { fileURLToPath } from "url";
@@ -3895,6 +4037,30 @@ function toJsonSchema(parameters) {
3895
4037
  }
3896
4038
  return parameters;
3897
4039
  }
4040
+ function extractTransformCallbacks(systemMessage) {
4041
+ if (!systemMessage || systemMessage.mode !== "customize" || !systemMessage.sections) {
4042
+ return { wirePayload: systemMessage, transformCallbacks: void 0 };
4043
+ }
4044
+ const transformCallbacks = /* @__PURE__ */ new Map();
4045
+ const wireSections = {};
4046
+ for (const [sectionId, override] of Object.entries(systemMessage.sections)) {
4047
+ if (!override) continue;
4048
+ if (typeof override.action === "function") {
4049
+ transformCallbacks.set(sectionId, override.action);
4050
+ wireSections[sectionId] = { action: "transform" };
4051
+ } else {
4052
+ wireSections[sectionId] = { action: override.action, content: override.content };
4053
+ }
4054
+ }
4055
+ if (transformCallbacks.size === 0) {
4056
+ return { wirePayload: systemMessage, transformCallbacks: void 0 };
4057
+ }
4058
+ const wirePayload = {
4059
+ ...systemMessage,
4060
+ sections: wireSections
4061
+ };
4062
+ return { wirePayload, transformCallbacks };
4063
+ }
3898
4064
  function getNodeExecPath() {
3899
4065
  if (process.versions.bun) {
3900
4066
  return "node";
@@ -3912,6 +4078,7 @@ var init_client = __esm({
3912
4078
  init_rpc();
3913
4079
  init_sdkProtocolVersion();
3914
4080
  init_session();
4081
+ init_telemetry();
3915
4082
  MIN_PROTOCOL_VERSION = 2;
3916
4083
  CopilotClient = class {
3917
4084
  cliProcess = null;
@@ -3926,6 +4093,8 @@ var init_client = __esm({
3926
4093
  options;
3927
4094
  isExternalServer = false;
3928
4095
  forceStopping = false;
4096
+ onListModels;
4097
+ onGetTraceContext;
3929
4098
  modelsCache = null;
3930
4099
  modelsCacheLock = Promise.resolve();
3931
4100
  sessionLifecycleHandlers = /* @__PURE__ */ new Set();
@@ -3991,8 +4160,10 @@ var init_client = __esm({
3991
4160
  if (options.isChildProcess) {
3992
4161
  this.isExternalServer = true;
3993
4162
  }
4163
+ this.onListModels = options.onListModels;
4164
+ this.onGetTraceContext = options.onGetTraceContext;
3994
4165
  this.options = {
3995
- cliPath: options.cliPath || getBundledCliPath(),
4166
+ cliPath: options.cliUrl ? void 0 : options.cliPath || getBundledCliPath(),
3996
4167
  cliArgs: options.cliArgs ?? [],
3997
4168
  cwd: options.cwd ?? process.cwd(),
3998
4169
  port: options.port || 0,
@@ -4002,11 +4173,12 @@ var init_client = __esm({
4002
4173
  cliUrl: options.cliUrl,
4003
4174
  logLevel: options.logLevel || "debug",
4004
4175
  autoStart: options.autoStart ?? true,
4005
- autoRestart: options.autoRestart ?? true,
4176
+ autoRestart: false,
4006
4177
  env: options.env ?? process.env,
4007
4178
  githubToken: options.githubToken,
4008
4179
  // Default useLoggedInUser to false when githubToken is provided, otherwise true
4009
- useLoggedInUser: options.useLoggedInUser ?? (options.githubToken ? false : true)
4180
+ useLoggedInUser: options.useLoggedInUser ?? (options.githubToken ? false : true),
4181
+ telemetry: options.telemetry
4010
4182
  };
4011
4183
  }
4012
4184
  /**
@@ -4258,36 +4430,13 @@ var init_client = __esm({
4258
4430
  throw new Error("Client not connected. Call start() first.");
4259
4431
  }
4260
4432
  }
4261
- const response = await this.connection.sendRequest("session.create", {
4262
- model: config.model,
4263
- sessionId: config.sessionId,
4264
- clientName: config.clientName,
4265
- reasoningEffort: config.reasoningEffort,
4266
- tools: config.tools?.map((tool) => ({
4267
- name: tool.name,
4268
- description: tool.description,
4269
- parameters: toJsonSchema(tool.parameters),
4270
- overridesBuiltInTool: tool.overridesBuiltInTool
4271
- })),
4272
- systemMessage: config.systemMessage,
4273
- availableTools: config.availableTools,
4274
- excludedTools: config.excludedTools,
4275
- provider: config.provider,
4276
- requestPermission: true,
4277
- requestUserInput: !!config.onUserInputRequest,
4278
- hooks: !!(config.hooks && Object.values(config.hooks).some(Boolean)),
4279
- workingDirectory: config.workingDirectory,
4280
- streaming: config.streaming,
4281
- mcpServers: config.mcpServers,
4282
- envValueMode: "direct",
4283
- customAgents: config.customAgents,
4284
- configDir: config.configDir,
4285
- skillDirectories: config.skillDirectories,
4286
- disabledSkills: config.disabledSkills,
4287
- infiniteSessions: config.infiniteSessions
4288
- });
4289
- const { sessionId, workspacePath } = response;
4290
- const session = new CopilotSession(sessionId, this.connection, workspacePath);
4433
+ const sessionId = config.sessionId ?? randomUUID();
4434
+ const session = new CopilotSession(
4435
+ sessionId,
4436
+ this.connection,
4437
+ void 0,
4438
+ this.onGetTraceContext
4439
+ );
4291
4440
  session.registerTools(config.tools);
4292
4441
  session.registerPermissionHandler(config.onPermissionRequest);
4293
4442
  if (config.onUserInputRequest) {
@@ -4296,7 +4445,54 @@ var init_client = __esm({
4296
4445
  if (config.hooks) {
4297
4446
  session.registerHooks(config.hooks);
4298
4447
  }
4448
+ const { wirePayload: wireSystemMessage, transformCallbacks } = extractTransformCallbacks(
4449
+ config.systemMessage
4450
+ );
4451
+ if (transformCallbacks) {
4452
+ session.registerTransformCallbacks(transformCallbacks);
4453
+ }
4454
+ if (config.onEvent) {
4455
+ session.on(config.onEvent);
4456
+ }
4299
4457
  this.sessions.set(sessionId, session);
4458
+ try {
4459
+ const response = await this.connection.sendRequest("session.create", {
4460
+ ...await getTraceContext(this.onGetTraceContext),
4461
+ model: config.model,
4462
+ sessionId,
4463
+ clientName: config.clientName,
4464
+ reasoningEffort: config.reasoningEffort,
4465
+ tools: config.tools?.map((tool) => ({
4466
+ name: tool.name,
4467
+ description: tool.description,
4468
+ parameters: toJsonSchema(tool.parameters),
4469
+ overridesBuiltInTool: tool.overridesBuiltInTool,
4470
+ skipPermission: tool.skipPermission
4471
+ })),
4472
+ systemMessage: wireSystemMessage,
4473
+ availableTools: config.availableTools,
4474
+ excludedTools: config.excludedTools,
4475
+ provider: config.provider,
4476
+ requestPermission: true,
4477
+ requestUserInput: !!config.onUserInputRequest,
4478
+ hooks: !!(config.hooks && Object.values(config.hooks).some(Boolean)),
4479
+ workingDirectory: config.workingDirectory,
4480
+ streaming: config.streaming,
4481
+ mcpServers: config.mcpServers,
4482
+ envValueMode: "direct",
4483
+ customAgents: config.customAgents,
4484
+ agent: config.agent,
4485
+ configDir: config.configDir,
4486
+ skillDirectories: config.skillDirectories,
4487
+ disabledSkills: config.disabledSkills,
4488
+ infiniteSessions: config.infiniteSessions
4489
+ });
4490
+ const { workspacePath } = response;
4491
+ session["_workspacePath"] = workspacePath;
4492
+ } catch (e) {
4493
+ this.sessions.delete(sessionId);
4494
+ throw e;
4495
+ }
4300
4496
  return session;
4301
4497
  }
4302
4498
  /**
@@ -4336,37 +4532,12 @@ var init_client = __esm({
4336
4532
  throw new Error("Client not connected. Call start() first.");
4337
4533
  }
4338
4534
  }
4339
- const response = await this.connection.sendRequest("session.resume", {
4535
+ const session = new CopilotSession(
4340
4536
  sessionId,
4341
- clientName: config.clientName,
4342
- model: config.model,
4343
- reasoningEffort: config.reasoningEffort,
4344
- systemMessage: config.systemMessage,
4345
- availableTools: config.availableTools,
4346
- excludedTools: config.excludedTools,
4347
- tools: config.tools?.map((tool) => ({
4348
- name: tool.name,
4349
- description: tool.description,
4350
- parameters: toJsonSchema(tool.parameters),
4351
- overridesBuiltInTool: tool.overridesBuiltInTool
4352
- })),
4353
- provider: config.provider,
4354
- requestPermission: true,
4355
- requestUserInput: !!config.onUserInputRequest,
4356
- hooks: !!(config.hooks && Object.values(config.hooks).some(Boolean)),
4357
- workingDirectory: config.workingDirectory,
4358
- configDir: config.configDir,
4359
- streaming: config.streaming,
4360
- mcpServers: config.mcpServers,
4361
- envValueMode: "direct",
4362
- customAgents: config.customAgents,
4363
- skillDirectories: config.skillDirectories,
4364
- disabledSkills: config.disabledSkills,
4365
- infiniteSessions: config.infiniteSessions,
4366
- disableResume: config.disableResume
4367
- });
4368
- const { sessionId: resumedSessionId, workspacePath } = response;
4369
- const session = new CopilotSession(resumedSessionId, this.connection, workspacePath);
4537
+ this.connection,
4538
+ void 0,
4539
+ this.onGetTraceContext
4540
+ );
4370
4541
  session.registerTools(config.tools);
4371
4542
  session.registerPermissionHandler(config.onPermissionRequest);
4372
4543
  if (config.onUserInputRequest) {
@@ -4375,7 +4546,55 @@ var init_client = __esm({
4375
4546
  if (config.hooks) {
4376
4547
  session.registerHooks(config.hooks);
4377
4548
  }
4378
- this.sessions.set(resumedSessionId, session);
4549
+ const { wirePayload: wireSystemMessage, transformCallbacks } = extractTransformCallbacks(
4550
+ config.systemMessage
4551
+ );
4552
+ if (transformCallbacks) {
4553
+ session.registerTransformCallbacks(transformCallbacks);
4554
+ }
4555
+ if (config.onEvent) {
4556
+ session.on(config.onEvent);
4557
+ }
4558
+ this.sessions.set(sessionId, session);
4559
+ try {
4560
+ const response = await this.connection.sendRequest("session.resume", {
4561
+ ...await getTraceContext(this.onGetTraceContext),
4562
+ sessionId,
4563
+ clientName: config.clientName,
4564
+ model: config.model,
4565
+ reasoningEffort: config.reasoningEffort,
4566
+ systemMessage: wireSystemMessage,
4567
+ availableTools: config.availableTools,
4568
+ excludedTools: config.excludedTools,
4569
+ tools: config.tools?.map((tool) => ({
4570
+ name: tool.name,
4571
+ description: tool.description,
4572
+ parameters: toJsonSchema(tool.parameters),
4573
+ overridesBuiltInTool: tool.overridesBuiltInTool,
4574
+ skipPermission: tool.skipPermission
4575
+ })),
4576
+ provider: config.provider,
4577
+ requestPermission: true,
4578
+ requestUserInput: !!config.onUserInputRequest,
4579
+ hooks: !!(config.hooks && Object.values(config.hooks).some(Boolean)),
4580
+ workingDirectory: config.workingDirectory,
4581
+ configDir: config.configDir,
4582
+ streaming: config.streaming,
4583
+ mcpServers: config.mcpServers,
4584
+ envValueMode: "direct",
4585
+ customAgents: config.customAgents,
4586
+ agent: config.agent,
4587
+ skillDirectories: config.skillDirectories,
4588
+ disabledSkills: config.disabledSkills,
4589
+ infiniteSessions: config.infiniteSessions,
4590
+ disableResume: config.disableResume
4591
+ });
4592
+ const { workspacePath } = response;
4593
+ session["_workspacePath"] = workspacePath;
4594
+ } catch (e) {
4595
+ this.sessions.delete(sessionId);
4596
+ throw e;
4597
+ }
4379
4598
  return session;
4380
4599
  }
4381
4600
  /**
@@ -4436,15 +4655,15 @@ var init_client = __esm({
4436
4655
  /**
4437
4656
  * List available models with their metadata.
4438
4657
  *
4658
+ * If an `onListModels` handler was provided in the client options,
4659
+ * it is called instead of querying the CLI server.
4660
+ *
4439
4661
  * Results are cached after the first successful call to avoid rate limiting.
4440
4662
  * The cache is cleared when the client disconnects.
4441
4663
  *
4442
- * @throws Error if not authenticated
4664
+ * @throws Error if not connected (when no custom handler is set)
4443
4665
  */
4444
4666
  async listModels() {
4445
- if (!this.connection) {
4446
- throw new Error("Client not connected");
4447
- }
4448
4667
  await this.modelsCacheLock;
4449
4668
  let resolveLock;
4450
4669
  this.modelsCacheLock = new Promise((resolve) => {
@@ -4454,10 +4673,18 @@ var init_client = __esm({
4454
4673
  if (this.modelsCache !== null) {
4455
4674
  return [...this.modelsCache];
4456
4675
  }
4457
- const result = await this.connection.sendRequest("models.list", {});
4458
- const response = result;
4459
- const models = response.models;
4460
- this.modelsCache = models;
4676
+ let models;
4677
+ if (this.onListModels) {
4678
+ models = await this.onListModels();
4679
+ } else {
4680
+ if (!this.connection) {
4681
+ throw new Error("Client not connected");
4682
+ }
4683
+ const result = await this.connection.sendRequest("models.list", {});
4684
+ const response = result;
4685
+ models = response.models;
4686
+ }
4687
+ this.modelsCache = [...models];
4461
4688
  return [...models];
4462
4689
  } finally {
4463
4690
  resolveLock();
@@ -4670,6 +4897,27 @@ var init_client = __esm({
4670
4897
  if (this.options.githubToken) {
4671
4898
  envWithoutNodeDebug.COPILOT_SDK_AUTH_TOKEN = this.options.githubToken;
4672
4899
  }
4900
+ if (!this.options.cliPath) {
4901
+ throw new Error(
4902
+ "Path to Copilot CLI is required. Please provide it via the cliPath option, or use cliUrl to rely on a remote CLI."
4903
+ );
4904
+ }
4905
+ if (this.options.telemetry) {
4906
+ const t = this.options.telemetry;
4907
+ envWithoutNodeDebug.COPILOT_OTEL_ENABLED = "true";
4908
+ if (t.otlpEndpoint !== void 0)
4909
+ envWithoutNodeDebug.OTEL_EXPORTER_OTLP_ENDPOINT = t.otlpEndpoint;
4910
+ if (t.filePath !== void 0)
4911
+ envWithoutNodeDebug.COPILOT_OTEL_FILE_EXPORTER_PATH = t.filePath;
4912
+ if (t.exporterType !== void 0)
4913
+ envWithoutNodeDebug.COPILOT_OTEL_EXPORTER_TYPE = t.exporterType;
4914
+ if (t.sourceName !== void 0)
4915
+ envWithoutNodeDebug.COPILOT_OTEL_SOURCE_NAME = t.sourceName;
4916
+ if (t.captureContent !== void 0)
4917
+ envWithoutNodeDebug.OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT = String(
4918
+ t.captureContent
4919
+ );
4920
+ }
4673
4921
  if (!existsSync(this.options.cliPath)) {
4674
4922
  throw new Error(
4675
4923
  `Copilot CLI not found at ${this.options.cliPath}. Ensure @github/copilot is installed.`
@@ -4769,8 +5017,6 @@ stderr: ${stderrOutput}`
4769
5017
  } else {
4770
5018
  reject(new Error(`CLI server exited with code ${code}`));
4771
5019
  }
4772
- } else if (this.options.autoRestart && this.state === "connected") {
4773
- void this.reconnect();
4774
5020
  }
4775
5021
  });
4776
5022
  setTimeout(() => {
@@ -4875,12 +5121,15 @@ stderr: ${stderrOutput}`
4875
5121
  "hooks.invoke",
4876
5122
  async (params) => await this.handleHooksInvoke(params)
4877
5123
  );
5124
+ this.connection.onRequest(
5125
+ "systemMessage.transform",
5126
+ async (params) => await this.handleSystemMessageTransform(params)
5127
+ );
4878
5128
  this.connection.onClose(() => {
4879
- if (this.state === "connected" && this.options.autoRestart) {
4880
- void this.reconnect();
4881
- }
5129
+ this.state = "disconnected";
4882
5130
  });
4883
5131
  this.connection.onError((_error) => {
5132
+ this.state = "disconnected";
4884
5133
  });
4885
5134
  }
4886
5135
  handleSessionEventNotification(notification) {
@@ -4939,6 +5188,16 @@ stderr: ${stderrOutput}`
4939
5188
  const output = await session._handleHooksInvoke(params.hookType, params.input);
4940
5189
  return { output };
4941
5190
  }
5191
+ async handleSystemMessageTransform(params) {
5192
+ if (!params || typeof params.sessionId !== "string" || !params.sections || typeof params.sections !== "object") {
5193
+ throw new Error("Invalid systemMessage.transform payload");
5194
+ }
5195
+ const session = this.sessions.get(params.sessionId);
5196
+ if (!session) {
5197
+ throw new Error(`Session not found: ${params.sessionId}`);
5198
+ }
5199
+ return await session._handleSystemMessageTransform(params.sections);
5200
+ }
4942
5201
  // ========================================================================
4943
5202
  // Protocol v2 backward-compatibility adapters
4944
5203
  // ========================================================================
@@ -4967,11 +5226,15 @@ stderr: ${stderrOutput}`
4967
5226
  };
4968
5227
  }
4969
5228
  try {
5229
+ const traceparent = params.traceparent;
5230
+ const tracestate = params.tracestate;
4970
5231
  const invocation = {
4971
5232
  sessionId: params.sessionId,
4972
5233
  toolCallId: params.toolCallId,
4973
5234
  toolName: params.toolName,
4974
- arguments: params.arguments
5235
+ arguments: params.arguments,
5236
+ traceparent,
5237
+ tracestate
4975
5238
  };
4976
5239
  const result = await handler(params.arguments, invocation);
4977
5240
  return { result: this.normalizeToolResultV2(result) };
@@ -5001,7 +5264,10 @@ stderr: ${stderrOutput}`
5001
5264
  try {
5002
5265
  const result = await session._handlePermissionRequestV2(params.permissionRequest);
5003
5266
  return { result };
5004
- } catch (_error) {
5267
+ } catch (error) {
5268
+ if (error instanceof Error && error.message === NO_RESULT_PERMISSION_V2_ERROR) {
5269
+ throw error;
5270
+ }
5005
5271
  return {
5006
5272
  result: {
5007
5273
  kind: "denied-no-approval-rule-and-could-not-request-from-user"
@@ -5031,17 +5297,6 @@ stderr: ${stderrOutput}`
5031
5297
  isToolResultObject(value) {
5032
5298
  return typeof value === "object" && value !== null && "textResultForLlm" in value && typeof value.textResultForLlm === "string" && "resultType" in value;
5033
5299
  }
5034
- /**
5035
- * Attempt to reconnect to the server
5036
- */
5037
- async reconnect() {
5038
- this.state = "disconnected";
5039
- try {
5040
- await this.stop();
5041
- await this.start();
5042
- } catch (_error) {
5043
- }
5044
- }
5045
5300
  };
5046
5301
  }
5047
5302
  });
@@ -5050,10 +5305,24 @@ stderr: ${stderrOutput}`
5050
5305
  function defineTool(name, config) {
5051
5306
  return { name, ...config };
5052
5307
  }
5053
- var approveAll;
5308
+ var SYSTEM_PROMPT_SECTIONS, approveAll;
5054
5309
  var init_types = __esm({
5055
5310
  "node_modules/@github/copilot-sdk/dist/types.js"() {
5056
5311
  "use strict";
5312
+ SYSTEM_PROMPT_SECTIONS = {
5313
+ identity: { description: "Agent identity preamble and mode statement" },
5314
+ tone: { description: "Response style, conciseness rules, output formatting preferences" },
5315
+ tool_efficiency: { description: "Tool usage patterns, parallel calling, batching guidelines" },
5316
+ environment_context: { description: "CWD, OS, git root, directory listing, available tools" },
5317
+ code_change_rules: { description: "Coding rules, linting/testing, ecosystem tools, style" },
5318
+ guidelines: { description: "Tips, behavioral best practices, behavioral guidelines" },
5319
+ safety: { description: "Environment limitations, prohibited actions, security policies" },
5320
+ tool_instructions: { description: "Per-tool usage instructions" },
5321
+ custom_instructions: { description: "Repository and organization custom instructions" },
5322
+ last_instructions: {
5323
+ description: "End-of-prompt instructions: parallel tool calling, persistence, task completion"
5324
+ }
5325
+ };
5057
5326
  approveAll = () => ({ kind: "approved" });
5058
5327
  }
5059
5328
  });
@@ -5063,6 +5332,7 @@ var dist_exports = {};
5063
5332
  __export(dist_exports, {
5064
5333
  CopilotClient: () => CopilotClient,
5065
5334
  CopilotSession: () => CopilotSession,
5335
+ SYSTEM_PROMPT_SECTIONS: () => SYSTEM_PROMPT_SECTIONS,
5066
5336
  approveAll: () => approveAll,
5067
5337
  defineTool: () => defineTool
5068
5338
  });
@@ -5076,7 +5346,7 @@ var init_dist = __esm({
5076
5346
  });
5077
5347
 
5078
5348
  // src/cli.ts
5079
- import { createRequire } from "module";
5349
+ import { createRequire as createRequire2 } from "module";
5080
5350
 
5081
5351
  // packages/core/src/config.ts
5082
5352
  var DEFAULT_MODEL = "claude-sonnet-4.6";
@@ -5332,6 +5602,18 @@ async function fileExists(filePath) {
5332
5602
  return false;
5333
5603
  }
5334
5604
  }
5605
+ async function canSafeWrite(filePath, force) {
5606
+ const resolved = path.resolve(filePath);
5607
+ if (await hasSymlinkAncestor(resolved)) return false;
5608
+ try {
5609
+ const stat = await fs.lstat(resolved);
5610
+ if (stat.isSymbolicLink()) return false;
5611
+ return force;
5612
+ } catch (error) {
5613
+ if (error.code === "ENOENT") return true;
5614
+ throw error;
5615
+ }
5616
+ }
5335
5617
  async function safeReadDir(dirPath) {
5336
5618
  try {
5337
5619
  return await fs.readdir(dirPath);
@@ -5339,10 +5621,47 @@ async function safeReadDir(dirPath) {
5339
5621
  return [];
5340
5622
  }
5341
5623
  }
5624
+ function stripJsonComments(text) {
5625
+ let result = "";
5626
+ let i = 0;
5627
+ while (i < text.length) {
5628
+ if (text[i] === '"') {
5629
+ let j = i + 1;
5630
+ while (j < text.length) {
5631
+ if (text[j] === "\\") {
5632
+ j += 2;
5633
+ continue;
5634
+ }
5635
+ if (text[j] === '"') {
5636
+ j++;
5637
+ break;
5638
+ }
5639
+ j++;
5640
+ }
5641
+ result += text.slice(i, j);
5642
+ i = j;
5643
+ continue;
5644
+ }
5645
+ if (text[i] === "/" && text[i + 1] === "/") {
5646
+ i += 2;
5647
+ while (i < text.length && text[i] !== "\n") i++;
5648
+ continue;
5649
+ }
5650
+ if (text[i] === "/" && text[i + 1] === "*") {
5651
+ i += 2;
5652
+ while (i < text.length && !(text[i] === "*" && text[i + 1] === "/")) i++;
5653
+ if (i < text.length) i += 2;
5654
+ continue;
5655
+ }
5656
+ result += text[i];
5657
+ i++;
5658
+ }
5659
+ return result;
5660
+ }
5342
5661
  async function readJson(filePath) {
5343
5662
  try {
5344
5663
  const raw = await fs.readFile(filePath, "utf8");
5345
- return JSON.parse(raw);
5664
+ return JSON.parse(stripJsonComments(raw));
5346
5665
  } catch {
5347
5666
  return void 0;
5348
5667
  }
@@ -5357,7 +5676,8 @@ var PACKAGE_MANAGERS = [
5357
5676
  { file: "pnpm-lock.yaml", name: "pnpm" },
5358
5677
  { file: "yarn.lock", name: "yarn" },
5359
5678
  { file: "package-lock.json", name: "npm" },
5360
- { file: "bun.lockb", name: "bun" }
5679
+ { file: "bun.lockb", name: "bun" },
5680
+ { file: "packages.lock.json", name: "nuget" }
5361
5681
  ];
5362
5682
  async function analyzeRepo(repoPath) {
5363
5683
  const files = await safeReadDir(repoPath);
@@ -5379,9 +5699,11 @@ async function analyzeRepo(repoPath) {
5379
5699
  const hasRequirements = files.includes("requirements.txt");
5380
5700
  const hasGoMod = files.includes("go.mod");
5381
5701
  const hasCargo = files.includes("Cargo.toml");
5382
- const hasCsproj = files.some(
5383
- (f) => f.endsWith(".csproj") || f.endsWith(".sln") || f.endsWith(".slnx")
5702
+ const hasDotnet = files.some(
5703
+ (f) => f.endsWith(".csproj") || f.endsWith(".fsproj") || f.endsWith(".sln") || f.endsWith(".slnx") || f === "global.json" || f === "Directory.Build.props"
5384
5704
  );
5705
+ const hasCsproj = hasDotnet && files.some((f) => f.endsWith(".csproj"));
5706
+ const hasFsproj = files.some((f) => f.endsWith(".fsproj"));
5385
5707
  const hasPomXml = files.includes("pom.xml");
5386
5708
  const hasBuildGradle = files.includes("build.gradle") || files.includes("build.gradle.kts");
5387
5709
  const hasGemfile = files.includes("Gemfile");
@@ -5396,7 +5718,8 @@ async function analyzeRepo(repoPath) {
5396
5718
  if (hasPyProject || hasRequirements) analysis.languages.push("Python");
5397
5719
  if (hasGoMod) analysis.languages.push("Go");
5398
5720
  if (hasCargo) analysis.languages.push("Rust");
5399
- if (hasCsproj) analysis.languages.push("C#");
5721
+ if (hasCsproj || hasDotnet && !hasFsproj) analysis.languages.push("C#");
5722
+ if (hasFsproj) analysis.languages.push("F#");
5400
5723
  if (hasPomXml || hasBuildGradle) analysis.languages.push("Java");
5401
5724
  if (hasGemfile) analysis.languages.push("Ruby");
5402
5725
  if (hasComposerJson) analysis.languages.push("PHP");
@@ -5412,6 +5735,10 @@ async function analyzeRepo(repoPath) {
5412
5735
  });
5413
5736
  analysis.frameworks.push(...detectFrameworks(deps, files));
5414
5737
  }
5738
+ if (hasDotnet) {
5739
+ const dotnetFrameworks = await detectDotnetFrameworks(repoPath);
5740
+ analysis.frameworks.push(...dotnetFrameworks);
5741
+ }
5415
5742
  const workspace = await detectWorkspace(repoPath, files, rootPackageJson);
5416
5743
  if (workspace) {
5417
5744
  analysis.workspaceType = workspace.type;
@@ -5472,6 +5799,44 @@ function detectFrameworks(deps, files) {
5472
5799
  if (deps.includes("fastify")) frameworks.push("Fastify");
5473
5800
  return frameworks;
5474
5801
  }
5802
+ async function detectDotnetFrameworks(repoPath) {
5803
+ const projectFiles = await fg("**/*.{csproj,fsproj}", {
5804
+ cwd: repoPath,
5805
+ onlyFiles: true,
5806
+ ignore: ["**/node_modules/**", "**/bin/**", "**/obj/**"]
5807
+ });
5808
+ const frameworks = [];
5809
+ for (const projFile of projectFiles) {
5810
+ try {
5811
+ const content = await fs2.readFile(path2.join(repoPath, projFile), "utf8");
5812
+ frameworks.push(...parseDotnetProject(content));
5813
+ } catch {
5814
+ }
5815
+ }
5816
+ return frameworks;
5817
+ }
5818
+ function parseDotnetProject(content) {
5819
+ const frameworks = [];
5820
+ const hasPackage = (pkg) => content.includes(`Include="${pkg}"`);
5821
+ if (content.includes('Sdk="Microsoft.NET.Sdk.Web"')) frameworks.push("ASP.NET Core");
5822
+ if (content.includes('Sdk="Microsoft.NET.Sdk.BlazorWebAssembly"'))
5823
+ frameworks.push("Blazor WebAssembly");
5824
+ if (hasPackage("Microsoft.AspNetCore") || hasPackage("Microsoft.AspNetCore.App"))
5825
+ frameworks.push("ASP.NET Core");
5826
+ if (hasPackage("Microsoft.AspNetCore.Components")) frameworks.push("Blazor");
5827
+ if (hasPackage("Microsoft.EntityFrameworkCore")) frameworks.push("Entity Framework");
5828
+ if (hasPackage("Microsoft.Maui.Controls")) frameworks.push(".NET MAUI");
5829
+ if (hasPackage("Xamarin.Forms") || hasPackage("Xamarin.Essentials")) frameworks.push("Xamarin");
5830
+ if (content.includes("<UseWPF>true</UseWPF>")) frameworks.push("WPF");
5831
+ if (content.includes("<UseWindowsForms>true</UseWindowsForms>")) frameworks.push("Windows Forms");
5832
+ if (hasPackage("xunit") || hasPackage("xunit.core")) frameworks.push("xUnit");
5833
+ if (hasPackage("NUnit") || hasPackage("nunit.framework")) frameworks.push("NUnit");
5834
+ if (hasPackage("MSTest.TestFramework")) frameworks.push("MSTest");
5835
+ if (frameworks.length === 0 && content.includes("<OutputType>Exe</OutputType>") && content.includes('Sdk="Microsoft.NET.Sdk"')) {
5836
+ frameworks.push("Console");
5837
+ }
5838
+ return frameworks;
5839
+ }
5475
5840
  async function safeReadFile(filePath) {
5476
5841
  try {
5477
5842
  return await fs2.readFile(filePath, "utf8");
@@ -6626,14 +6991,14 @@ async function checkReposForInstructions(token, repos, onProgress) {
6626
6991
  }
6627
6992
 
6628
6993
  // packages/core/src/services/batch.ts
6629
- import path9 from "path";
6994
+ import path10 from "path";
6630
6995
 
6631
6996
  // packages/core/src/utils/pr.ts
6632
6997
  function buildInstructionsPrBody() {
6633
6998
  return [
6634
- "## \u{1F916} Copilot Instructions Added",
6999
+ "## \u{1F916} Instructions Added",
6635
7000
  "",
6636
- "This PR adds a `.github/copilot-instructions.md` file to help GitHub Copilot understand this codebase better.",
7001
+ "This PR adds a `.github/copilot-instructions.md` file to help AI coding assistants understand this codebase better.",
6637
7002
  "",
6638
7003
  "### What's Included",
6639
7004
  "",
@@ -6645,7 +7010,7 @@ function buildInstructionsPrBody() {
6645
7010
  "",
6646
7011
  "### Benefits",
6647
7012
  "",
6648
- "With these instructions, Copilot will:",
7013
+ "With these instructions, AI coding assistants will:",
6649
7014
  "- Generate more contextually-aware code suggestions",
6650
7015
  "- Follow project-specific patterns and conventions",
6651
7016
  "- Understand the codebase structure",
@@ -6664,7 +7029,7 @@ function buildFullPrBody() {
6664
7029
  "",
6665
7030
  "| File | Purpose |",
6666
7031
  "|------|---------|",
6667
- "| `.github/copilot-instructions.md` | Custom instructions for GitHub Copilot |",
7032
+ "| `.github/copilot-instructions.md` | Custom instructions for AI coding assistants |",
6668
7033
  "| `.vscode/settings.json` | VS Code settings for optimal AI assistance |",
6669
7034
  "| `.vscode/mcp.json` | Model Context Protocol server configuration |",
6670
7035
  "",
@@ -6678,7 +7043,7 @@ function buildFullPrBody() {
6678
7043
  "",
6679
7044
  "### Benefits",
6680
7045
  "",
6681
- "With these configurations, Copilot will:",
7046
+ "With these configurations, AI coding assistants will:",
6682
7047
  "- Generate more contextually-aware code suggestions",
6683
7048
  "- Follow project-specific patterns and conventions",
6684
7049
  "- Understand the codebase structure",
@@ -6688,7 +7053,7 @@ function buildFullPrBody() {
6688
7053
  "",
6689
7054
  "1. Merge this PR",
6690
7055
  "2. Open the project in VS Code",
6691
- "3. Start chatting with Copilot \u2014 it now understands your project!",
7056
+ "3. Start using AI assistants \u2014 they now understand your project!",
6692
7057
  "",
6693
7058
  "---",
6694
7059
  "*Generated by [AgentRC](https://github.com/microsoft/agentrc) - Prime your repos for AI*"
@@ -6910,7 +7275,7 @@ async function checkReposForInstructions2(token, repos, onProgress) {
6910
7275
 
6911
7276
  // packages/core/src/services/instructions.ts
6912
7277
  import fs5 from "fs/promises";
6913
- import path6 from "path";
7278
+ import path7 from "path";
6914
7279
 
6915
7280
  // packages/core/src/services/copilot.ts
6916
7281
  import fs4 from "fs/promises";
@@ -6953,8 +7318,32 @@ async function assertCopilotCliReady() {
6953
7318
  async function listCopilotModels() {
6954
7319
  const config = await assertCopilotCliReady();
6955
7320
  const [cmd, args2] = buildExecArgs(config, ["--help"]);
6956
- const { stdout } = await execFileAsync2(cmd, args2, { timeout: 5e3 });
6957
- return extractModelChoices(stdout);
7321
+ let stdout = "";
7322
+ let stderr = "";
7323
+ let execError = null;
7324
+ try {
7325
+ const result = await execFileAsync2(cmd, args2, { timeout: 5e3 });
7326
+ stdout = result.stdout;
7327
+ stderr = result.stderr;
7328
+ } catch (err) {
7329
+ execError = err;
7330
+ const e = err;
7331
+ stdout = e.stdout ?? "";
7332
+ stderr = e.stderr ?? "";
7333
+ }
7334
+ const fromStdout = extractModelChoices(stdout);
7335
+ if (fromStdout.length > 0) return fromStdout;
7336
+ const fromStderr = extractModelChoices(stderr);
7337
+ if (fromStderr.length > 0) return fromStderr;
7338
+ if (execError) {
7339
+ const e = execError;
7340
+ const details = e.stderr || e.stdout;
7341
+ const detailMsg = details ? `
7342
+ Copilot CLI output:
7343
+ ${details}` : "";
7344
+ throw new Error(`Failed to list Copilot models: ${e.message}${detailMsg}`);
7345
+ }
7346
+ return [];
6958
7347
  }
6959
7348
  function buildExecArgs(config, extraArgs) {
6960
7349
  if (config.cliArgs && config.cliArgs.length > 0) {
@@ -7163,7 +7552,9 @@ function normalizeError(error) {
7163
7552
  }
7164
7553
  function shouldFallbackToExternalServer(error) {
7165
7554
  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");
7555
+ return message.includes("unknown option '--headless'") || message.includes("unknown option '--no-auto-update'") || // SDK's internal CLI resolution couldn't find the binary
7556
+ message.includes("copilot cli not found") || // Node's spawn() can't execute .bat/.cmd directly on Windows
7557
+ message.includes("spawn einval");
7167
7558
  }
7168
7559
  async function startExternalServer(cliConfig) {
7169
7560
  const [cmd, args2] = buildExecArgs(cliConfig, ["--headless", "--log-level", "debug"]);
@@ -7281,8 +7672,11 @@ async function createCopilotClient(cliConfig) {
7281
7672
  const desc = cliConfig.cliArgs ? `${cliConfig.cliPath} ${cliConfig.cliArgs.join(" ")}` : cliConfig.cliPath;
7282
7673
  logCopilotDebug(`creating SDK client with cliPath=${desc} useStdio=false`);
7283
7674
  const isNpx = /\bnpx(?:\.cmd)?$/iu.test(cliConfig.cliPath);
7284
- if (isNpx) {
7285
- logCopilotDebug("npx wrapper detected; using external server mode");
7675
+ const isBatShim = process.platform === "win32" && /\.(?:bat|cmd)$/iu.test(cliConfig.cliPath);
7676
+ if (isNpx || isBatShim) {
7677
+ logCopilotDebug(
7678
+ `${isNpx ? "npx wrapper" : ".bat/.cmd shim"} detected; using external server mode directly`
7679
+ );
7286
7680
  const external = await startExternalServer(cliConfig);
7287
7681
  const client = new sdk.CopilotClient({ cliUrl: external.cliUrl });
7288
7682
  try {
@@ -7325,6 +7719,25 @@ async function createCopilotClient(cliConfig) {
7325
7719
  }
7326
7720
  }
7327
7721
 
7722
+ // packages/core/src/services/skills.ts
7723
+ import path6 from "path";
7724
+ import { fileURLToPath as fileURLToPath2 } from "url";
7725
+ var overrideSkillsDir;
7726
+ function getBuiltinSkillsDir() {
7727
+ if (overrideSkillsDir) {
7728
+ return overrideSkillsDir;
7729
+ }
7730
+ const thisFile = fileURLToPath2(import.meta.url);
7731
+ const thisDir = path6.dirname(thisFile);
7732
+ if (thisDir.includes(path6.join("packages", "core", "src"))) {
7733
+ return path6.resolve(thisDir, "..", "..", "..", "..", "plugin", "skills");
7734
+ }
7735
+ return path6.resolve(thisDir, "skills");
7736
+ }
7737
+ function getSkillDirectory(_skillName) {
7738
+ return getBuiltinSkillsDir();
7739
+ }
7740
+
7328
7741
  // packages/core/src/services/instructions.ts
7329
7742
  async function detectExistingInstructions(repoPath, detailDirName = ".agents") {
7330
7743
  const { agentsMdFiles, claudeMdFiles } = await findInstructionMarkerFiles(repoPath);
@@ -7348,7 +7761,7 @@ async function findInstructionMarkerFiles(repoPath) {
7348
7761
  claudeMdFiles.push(relPath ? `${relPath}/${entry.name}` : entry.name);
7349
7762
  }
7350
7763
  } else if (entry.isDirectory()) {
7351
- await walk(path6.join(dir, entry.name), relPath ? `${relPath}/${entry.name}` : entry.name);
7764
+ await walk(path7.join(dir, entry.name), relPath ? `${relPath}/${entry.name}` : entry.name);
7352
7765
  }
7353
7766
  }
7354
7767
  }
@@ -7356,7 +7769,7 @@ async function findInstructionMarkerFiles(repoPath) {
7356
7769
  return { agentsMdFiles: agentsMdFiles.sort(), claudeMdFiles: claudeMdFiles.sort() };
7357
7770
  }
7358
7771
  async function findModularInstructionFiles(repoPath) {
7359
- const dir = path6.join(repoPath, ".github", "instructions");
7772
+ const dir = path7.join(repoPath, ".github", "instructions");
7360
7773
  const entries = await fs5.readdir(dir, { withFileTypes: true }).catch(() => []);
7361
7774
  return entries.filter((e) => !e.isSymbolicLink() && e.isFile() && e.name.endsWith(".instructions.md")).map((e) => `.github/instructions/${e.name}`).sort();
7362
7775
  }
@@ -7370,7 +7783,7 @@ async function findDetailFiles(repoPath, detailDirName) {
7370
7783
  if (entry.isSymbolicLink()) continue;
7371
7784
  if (entry.isDirectory()) {
7372
7785
  if (entry.name === detailDirName) {
7373
- const detailDir = path6.join(dir, entry.name);
7786
+ const detailDir = path7.join(dir, entry.name);
7374
7787
  const detailEntries = await fs5.readdir(detailDir, { withFileTypes: true }).catch(() => []);
7375
7788
  for (const de of detailEntries) {
7376
7789
  if (!de.isSymbolicLink() && de.isFile() && de.name.endsWith(".md")) {
@@ -7379,7 +7792,7 @@ async function findDetailFiles(repoPath, detailDirName) {
7379
7792
  }
7380
7793
  }
7381
7794
  } else {
7382
- await walk(path6.join(dir, entry.name), relPath ? `${relPath}/${entry.name}` : entry.name);
7795
+ await walk(path7.join(dir, entry.name), relPath ? `${relPath}/${entry.name}` : entry.name);
7383
7796
  }
7384
7797
  }
7385
7798
  }
@@ -7461,11 +7874,11 @@ function isCopilotAuthError(error) {
7461
7874
  return error instanceof Error && error.message.includes("Copilot CLI not logged in.");
7462
7875
  }
7463
7876
  function resolveAreaWorkingDirectory(repoPath, area) {
7464
- const repoRoot = path6.resolve(repoPath);
7877
+ const repoRoot = path7.resolve(repoPath);
7465
7878
  const rawWorkingDirectory = area?.workingDirectory ?? area?.path;
7466
7879
  if (!rawWorkingDirectory) return repoRoot;
7467
- const resolved = area?.workingDirectory ? path6.resolve(repoRoot, rawWorkingDirectory) : path6.isAbsolute(rawWorkingDirectory) ? path6.resolve(rawWorkingDirectory) : path6.resolve(repoRoot, rawWorkingDirectory);
7468
- if (resolved !== repoRoot && !resolved.startsWith(repoRoot + path6.sep)) {
7880
+ const resolved = area?.workingDirectory ? path7.resolve(repoRoot, rawWorkingDirectory) : path7.isAbsolute(rawWorkingDirectory) ? path7.resolve(rawWorkingDirectory) : path7.resolve(repoRoot, rawWorkingDirectory);
7881
+ if (resolved !== repoRoot && !resolved.startsWith(repoRoot + path7.sep)) {
7469
7882
  throw new Error(`Invalid workingDirectory "${rawWorkingDirectory}": escapes repo boundary`);
7470
7883
  }
7471
7884
  return resolved;
@@ -7497,6 +7910,7 @@ async function generateCopilotInstructions(options) {
7497
7910
  const preferredModel = options.model ?? DEFAULT_MODEL;
7498
7911
  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.";
7499
7912
  const { tool: emitTool, getContent } = await createEmitTool();
7913
+ const rootSkillDir = getSkillDirectory("root-instructions");
7500
7914
  const session = await client.createSession({
7501
7915
  model: preferredModel,
7502
7916
  streaming: true,
@@ -7507,7 +7921,8 @@ async function generateCopilotInstructions(options) {
7507
7921
  tools: [emitTool],
7508
7922
  excludedTools: INSTRUCTION_GENERATION_EXCLUDED_TOOLS,
7509
7923
  onPermissionRequest: READ_ONLY_PERMISSION_HANDLER,
7510
- infiniteSessions: { enabled: false }
7924
+ infiniteSessions: { enabled: false },
7925
+ skillDirectories: [rootSkillDir]
7511
7926
  });
7512
7927
  await trySetAutopilot(session);
7513
7928
  let content = "";
@@ -7528,22 +7943,8 @@ async function generateCopilotInstructions(options) {
7528
7943
  sessionError = getSessionError(errorMsg);
7529
7944
  }
7530
7945
  });
7531
- const prompt = `Analyze this codebase and generate a .github/copilot-instructions.md file.
7532
-
7533
- Fan out multiple Explore subagents to map out the codebase in parallel:
7534
- 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.
7536
- 3. Understand the structure: list key directories
7537
- 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
-
7539
- Generate concise instructions (~20-50 lines) covering:
7540
- - Tech stack and architecture
7541
- - Build/test commands
7542
- - Project-specific conventions
7543
- - Key files/directories
7544
- - 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)
7545
- ${existingSection}
7546
- When you have the complete markdown content, call the \`emit_file_content\` tool with it. Do NOT output the file content directly in chat.`;
7946
+ const prompt = `/root-instructions Analyze this codebase and generate a .github/copilot-instructions.md file.
7947
+ ${existingSection}`;
7547
7948
  progress("Analyzing codebase...");
7548
7949
  let sendError;
7549
7950
  try {
@@ -7578,8 +7979,9 @@ async function generateAreaInstructions(options) {
7578
7979
  const applyToStr = applyToPatterns.join(", ");
7579
7980
  progress(`Creating session for area "${area.name}"...`);
7580
7981
  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.`;
7982
+ 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
7983
  const { tool: emitTool, getContent } = await createEmitTool();
7984
+ const areaSkillDir = getSkillDirectory("area-instructions");
7583
7985
  const session = await client.createSession({
7584
7986
  model: preferredModel,
7585
7987
  streaming: true,
@@ -7590,7 +7992,8 @@ async function generateAreaInstructions(options) {
7590
7992
  tools: [emitTool],
7591
7993
  excludedTools: INSTRUCTION_GENERATION_EXCLUDED_TOOLS,
7592
7994
  onPermissionRequest: READ_ONLY_PERMISSION_HANDLER,
7593
- infiniteSessions: { enabled: false }
7995
+ infiniteSessions: { enabled: false },
7996
+ skillDirectories: [areaSkillDir]
7594
7997
  });
7595
7998
  await trySetAutopilot(session);
7596
7999
  let content = "";
@@ -7611,30 +8014,13 @@ async function generateAreaInstructions(options) {
7611
8014
  sessionError = getSessionError(errorMsg);
7612
8015
  }
7613
8016
  });
7614
- const prompt = `Analyze the "${area.name}" area of this codebase and generate a file-based instruction file.
8017
+ const prompt = `/area-instructions Analyze the "${area.name}" area of this codebase and generate an area instruction file.
7615
8018
 
7616
8019
  This area covers files matching: ${applyToStr}
7617
8020
  ${area.description ? `Description: ${area.description}` : ""}
7618
-
7619
- Use tools to explore ONLY the files and directories within this area:
7620
- 1. List the key files: glob for ${applyToPatterns.map((p) => `"${p}"`).join(", ")}
7621
- 2. Identify the tech stack, dependencies, and frameworks used in this area
7622
- 3. Look at key source files to understand patterns and conventions specific to this area
7623
-
7624
- Generate concise instructions (~10-30 lines) covering:
7625
- - What this area does and its role in the overall project
7626
- - Area-specific tech stack, dependencies, and frameworks
7627
- - Coding conventions and patterns specific to this area
7628
- - Build/test commands relevant to this area (if different from root)
7629
- - Key files and directory structure within this area
7630
-
7631
- IMPORTANT:
7632
- - Focus ONLY on this specific area, not the whole repo
7633
- - Do NOT repeat repo-wide information (that goes in the root copilot-instructions.md)
7634
- - Keep it complementary to root instructions
7635
- ${existingSection ? `- Do NOT duplicate content already covered by existing instruction files
7636
- ${existingSection}` : ""}
7637
- - When you have the complete markdown content, call the \`emit_file_content\` tool with it. Do NOT output the file content directly in chat.`;
8021
+ ${existingSection ? `
8022
+ Do NOT duplicate content already covered by existing instruction files
8023
+ ${existingSection}` : ""}`;
7638
8024
  progress(`Analyzing area "${area.name}"...`);
7639
8025
  let sendError;
7640
8026
  try {
@@ -7671,7 +8057,7 @@ ${body}
7671
8057
  `;
7672
8058
  }
7673
8059
  function areaInstructionPath(repoPath, area) {
7674
- return path6.join(
8060
+ return path7.join(
7675
8061
  repoPath,
7676
8062
  ".github",
7677
8063
  "instructions",
@@ -7681,7 +8067,7 @@ function areaInstructionPath(repoPath, area) {
7681
8067
  async function writeAreaInstruction(repoPath, area, body, force) {
7682
8068
  const filePath = areaInstructionPath(repoPath, area);
7683
8069
  if (!body.trim()) return { status: "empty", filePath };
7684
- await ensureDir(path6.dirname(filePath));
8070
+ await ensureDir(path7.dirname(filePath));
7685
8071
  const { wrote, reason } = await safeWriteFile(
7686
8072
  filePath,
7687
8073
  buildAreaInstructionContent(area, body),
@@ -7693,13 +8079,13 @@ async function writeAreaInstruction(repoPath, area, body, force) {
7693
8079
  return { status: "written", filePath };
7694
8080
  }
7695
8081
  async function writeInstructionFile(repoPath, relativePath, content, force) {
7696
- const resolvedRoot = path6.resolve(repoPath);
7697
- const filePath = path6.resolve(repoPath, relativePath);
7698
- if (!filePath.startsWith(resolvedRoot + path6.sep) && filePath !== resolvedRoot) {
8082
+ const resolvedRoot = path7.resolve(repoPath);
8083
+ const filePath = path7.resolve(repoPath, relativePath);
8084
+ if (!filePath.startsWith(resolvedRoot + path7.sep) && filePath !== resolvedRoot) {
7699
8085
  throw new Error(`Invalid path: escapes repository root (${relativePath})`);
7700
8086
  }
7701
8087
  if (!content.trim()) return { status: "empty", filePath };
7702
- await ensureDir(path6.dirname(filePath));
8088
+ await ensureDir(path7.dirname(filePath));
7703
8089
  const { wrote, reason } = await safeWriteFile(filePath, content, Boolean(force));
7704
8090
  if (!wrote) {
7705
8091
  return { status: reason === "symlink" ? "symlink" : "skipped", filePath };
@@ -7783,6 +8169,7 @@ async function generateNestedHub(client, options) {
7783
8169
  const existingCtx = await detectExistingInstructions(options.repoPath, options.detailDir);
7784
8170
  const existingSection = buildExistingInstructionsSection(existingCtx);
7785
8171
  const { tool: emitTool, getContent } = await createEmitTool();
8172
+ const nestedSkillDir = getSkillDirectory("nested-hub");
7786
8173
  const session = await client.createSession({
7787
8174
  model,
7788
8175
  streaming: true,
@@ -7793,7 +8180,8 @@ async function generateNestedHub(client, options) {
7793
8180
  tools: [emitTool],
7794
8181
  excludedTools: INSTRUCTION_GENERATION_EXCLUDED_TOOLS,
7795
8182
  onPermissionRequest: READ_ONLY_PERMISSION_HANDLER,
7796
- infiniteSessions: { enabled: false }
8183
+ infiniteSessions: { enabled: false },
8184
+ skillDirectories: [nestedSkillDir]
7797
8185
  });
7798
8186
  await trySetAutopilot(session);
7799
8187
  let content = "";
@@ -7825,29 +8213,14 @@ ${options.childAreas.map((c) => `- ${c.name} (${c.path ?? "unknown path"})`).joi
7825
8213
  Include a "## Sub-Projects" section with links to each child's AGENTS.md.` : "";
7826
8214
  const parentContext = options.area?.parentArea ? `
7827
8215
  This is a sub-project of "${options.area.parentArea}". Include a note linking back to the parent area.` : "";
7828
- const prompt = `Analyze this codebase and generate a lean AGENTS.md hub file (~90-120 lines).${areaContext}${parentContext}
7829
-
7830
- Use tools to explore the codebase structure, tech stack, and conventions.
8216
+ const prompt = `/nested-hub Generate a lean AGENTS.md hub file (~90-120 lines).${areaContext}${parentContext}
7831
8217
 
7832
- The hub should contain:
7833
- - Project overview and purpose
7834
- - Key concepts and architecture
7835
- - Coding conventions and guardrails
7836
- - A "## Detailed Instructions" section listing links to detail files in \`${options.detailDir}/\`${childContext}
8218
+ Detail files go in \`${options.detailDir}/\`.${childContext}
7837
8219
 
7838
- At the END of your output, emit a fenced JSON block with recommended topics for detail files:
7839
- \`\`\`json
7840
- [{"slug":"testing","title":"Testing Guide","description":"How to write and run tests"},{"slug":"architecture","title":"Architecture","description":"Codebase structure and patterns"}]
7841
- \`\`\`
7842
-
7843
- Recommend 3-5 topics that would benefit from deep-dive detail files. Each slug becomes a filename: \`${options.detailDir}/{slug}.md\`.
7844
-
7845
- IMPORTANT:
7846
- - Keep the hub LEAN \u2014 overview and guardrails only, details go in separate files
7847
- - The JSON block will be parsed and removed from the final output
7848
- ${existingSection ? `- Do NOT duplicate content from existing instruction files
7849
- ${existingSection}` : ""}
7850
- - 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.`;
8220
+ Recommend 3-5 topics for deep-dive detail files. Each slug becomes: \`${options.detailDir}/{slug}.md\`.
8221
+ ${existingSection ? `
8222
+ Do NOT duplicate content from existing instruction files
8223
+ ${existingSection}` : ""}`;
7851
8224
  let sendError;
7852
8225
  try {
7853
8226
  await session.sendAndWait({ prompt }, 18e4);
@@ -7871,6 +8244,7 @@ async function generateNestedDetail(client, options) {
7871
8244
  });
7872
8245
  const model = options.model ?? DEFAULT_MODEL;
7873
8246
  const { tool: emitTool, getContent } = await createEmitTool();
8247
+ const detailSkillDir = getSkillDirectory("nested-detail");
7874
8248
  const session = await client.createSession({
7875
8249
  model,
7876
8250
  streaming: true,
@@ -7881,7 +8255,8 @@ async function generateNestedDetail(client, options) {
7881
8255
  tools: [emitTool],
7882
8256
  excludedTools: INSTRUCTION_GENERATION_EXCLUDED_TOOLS,
7883
8257
  onPermissionRequest: READ_ONLY_PERMISSION_HANDLER,
7884
- infiniteSessions: { enabled: false }
8258
+ infiniteSessions: { enabled: false },
8259
+ skillDirectories: [detailSkillDir]
7885
8260
  });
7886
8261
  await trySetAutopilot(session);
7887
8262
  let content = "";
@@ -7903,22 +8278,11 @@ async function generateNestedDetail(client, options) {
7903
8278
  }
7904
8279
  });
7905
8280
  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.";
7906
- const prompt = `Generate a deep-dive instruction file about "${options.topic.title}" for this codebase.
8281
+ const prompt = `/nested-detail Generate a deep-dive instruction file about "${options.topic.title}" for this codebase.
7907
8282
  ${areaContext}
7908
8283
 
7909
8284
  Topic: ${options.topic.title}
7910
- Description: ${options.topic.description}
7911
-
7912
- Use tools to explore the codebase and understand the specific patterns, APIs, and conventions related to this topic.
7913
-
7914
- The file should:
7915
- - Start with \`# ${options.topic.title}\`
7916
- - Include \`**When to read:** {one-line trigger condition}\` right after the heading
7917
- - Cover ~50-100 lines of practical, actionable guidance
7918
- - Include code patterns and examples found in the actual codebase
7919
- - Be specific to this codebase, not generic advice
7920
-
7921
- When you have the complete markdown content, call the \`emit_file_content\` tool with it. Do NOT output the content directly in chat.`;
8285
+ Description: ${options.topic.description}`;
7922
8286
  let sendError;
7923
8287
  try {
7924
8288
  await session.sendAndWait({ prompt }, 18e4);
@@ -7953,7 +8317,7 @@ async function generateNestedInstructions(options) {
7953
8317
  onProgress: options.onProgress
7954
8318
  });
7955
8319
  const basePath = options.area?.path ?? ".";
7956
- const hubRelativePath = path6.join(basePath, "AGENTS.md");
8320
+ const hubRelativePath = path7.join(basePath, "AGENTS.md");
7957
8321
  let finalHubContent = hubContent;
7958
8322
  if (options.area) {
7959
8323
  finalHubContent = `${buildAreaFrontmatter(options.area)}
@@ -7977,7 +8341,7 @@ ${hubContent}`;
7977
8341
  });
7978
8342
  if (detailContent) {
7979
8343
  result.details.push({
7980
- relativePath: path6.join(basePath, options.detailDir, `${topic.slug}.md`),
8344
+ relativePath: path7.join(basePath, options.detailDir, `${topic.slug}.md`),
7981
8345
  content: detailContent,
7982
8346
  topic: topic.title
7983
8347
  });
@@ -7992,7 +8356,7 @@ ${hubContent}`;
7992
8356
  }
7993
8357
  if (options.claudeMd) {
7994
8358
  result.claudeMd = {
7995
- relativePath: path6.join(basePath, "CLAUDE.md"),
8359
+ relativePath: path7.join(basePath, "CLAUDE.md"),
7996
8360
  content: "@AGENTS.md\n"
7997
8361
  };
7998
8362
  }
@@ -8015,11 +8379,11 @@ async function generateNestedAreaInstructions(options) {
8015
8379
 
8016
8380
  // packages/core/src/services/readiness.ts
8017
8381
  import fs7 from "fs/promises";
8018
- import path8 from "path";
8382
+ import path9 from "path";
8019
8383
 
8020
8384
  // packages/core/src/services/policy.ts
8021
8385
  import fs6 from "fs/promises";
8022
- import path7 from "path";
8386
+ import path8 from "path";
8023
8387
  var DEFAULT_PASS_RATE = 0.8;
8024
8388
  function validatePolicyConfig(obj, source, format = "module") {
8025
8389
  if (typeof obj !== "object" || obj === null) {
@@ -8112,8 +8476,8 @@ function parsePolicySources(raw) {
8112
8476
  }
8113
8477
  async function loadPolicy(source, options) {
8114
8478
  const jsonOnly = options?.jsonOnly ?? false;
8115
- if (source.startsWith(".") || path7.isAbsolute(source)) {
8116
- const resolved = path7.resolve(source);
8479
+ if (source.startsWith(".") || path8.isAbsolute(source)) {
8480
+ const resolved = path8.resolve(source);
8117
8481
  if (resolved.endsWith(".json")) {
8118
8482
  const data = await readJson(resolved);
8119
8483
  if (!data) {
@@ -8140,7 +8504,7 @@ async function loadPolicy(source, options) {
8140
8504
  }
8141
8505
  try {
8142
8506
  const raw = await fs6.readFile(resolved, "utf8");
8143
- const data = JSON.parse(raw);
8507
+ const data = JSON.parse(stripJsonComments(raw));
8144
8508
  return validatePolicyConfig(data, source, "json");
8145
8509
  } catch {
8146
8510
  throw new Error(
@@ -8733,7 +9097,7 @@ function getLevelName(level) {
8733
9097
  function getLevelDescription(level) {
8734
9098
  const descriptions = {
8735
9099
  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.",
9100
+ 2: "README, CONTRIBUTING guide, and custom instructions exist. Agents understand project context and conventions.",
8737
9101
  3: "CI/CD, security policies, CODEOWNERS, and observability are configured. Agents operate within well-defined guardrails.",
8738
9102
  4: "MCP servers, custom agents, and AI skills are set up. Agents have deep integration with project-specific tools and workflows.",
8739
9103
  5: "Full AI-native development: agents can independently plan, implement, test, and ship changes with minimal human oversight."
@@ -8744,7 +9108,7 @@ async function runReadinessReport(options) {
8744
9108
  const repoPath = options.repoPath;
8745
9109
  const analysis = await analyzeRepo(repoPath);
8746
9110
  const rootFiles = await safeReadDir(repoPath);
8747
- const rootPackageJson = await readJson(path8.join(repoPath, "package.json"));
9111
+ const rootPackageJson = await readJson(path9.join(repoPath, "package.json"));
8748
9112
  const apps = analysis.apps?.length ? analysis.apps : [];
8749
9113
  const context = {
8750
9114
  repoPath,
@@ -9053,7 +9417,7 @@ function buildCriteria() {
9053
9417
  impact: "medium",
9054
9418
  effort: "low",
9055
9419
  check: async (context) => {
9056
- const found = await fileExists(path8.join(context.repoPath, "CONTRIBUTING.md"));
9420
+ const found = await fileExists(path9.join(context.repoPath, "CONTRIBUTING.md"));
9057
9421
  return {
9058
9422
  status: found ? "pass" : "fail",
9059
9423
  reason: found ? void 0 : "Missing CONTRIBUTING.md for contributor workflows."
@@ -9154,7 +9518,7 @@ function buildCriteria() {
9154
9518
  impact: "high",
9155
9519
  effort: "low",
9156
9520
  check: async (context) => {
9157
- const found = await fileExists(path8.join(context.repoPath, "SECURITY.md"));
9521
+ const found = await fileExists(path9.join(context.repoPath, "SECURITY.md"));
9158
9522
  return {
9159
9523
  status: found ? "pass" : "fail",
9160
9524
  reason: found ? void 0 : "Missing SECURITY.md policy."
@@ -9170,7 +9534,7 @@ function buildCriteria() {
9170
9534
  impact: "medium",
9171
9535
  effort: "medium",
9172
9536
  check: async (context) => {
9173
- const found = await fileExists(path8.join(context.repoPath, ".github", "dependabot.yml"));
9537
+ const found = await fileExists(path9.join(context.repoPath, ".github", "dependabot.yml"));
9174
9538
  return {
9175
9539
  status: found ? "pass" : "fail",
9176
9540
  reason: found ? void 0 : "Missing .github/dependabot.yml configuration."
@@ -9198,7 +9562,7 @@ function buildCriteria() {
9198
9562
  },
9199
9563
  {
9200
9564
  id: "custom-instructions",
9201
- title: "Custom AI instructions or agent guidance",
9565
+ title: "Custom instructions or agent guidance",
9202
9566
  pillar: "ai-tooling",
9203
9567
  level: 1,
9204
9568
  scope: "repo",
@@ -9209,7 +9573,7 @@ function buildCriteria() {
9209
9573
  if (rootFound.length === 0) {
9210
9574
  return {
9211
9575
  status: "fail",
9212
- reason: "Missing custom AI instructions (e.g. copilot-instructions.md, CLAUDE.md, AGENTS.md, .cursorrules).",
9576
+ reason: "Missing custom instructions (e.g. copilot-instructions.md, CLAUDE.md, AGENTS.md, .cursorrules).",
9213
9577
  evidence: [
9214
9578
  "copilot-instructions.md",
9215
9579
  "CLAUDE.md",
@@ -9225,13 +9589,13 @@ function buildCriteria() {
9225
9589
  if (fileBasedInstructions.length === 0) {
9226
9590
  return {
9227
9591
  status: "pass",
9228
- reason: `Root instructions found, but no file-based instructions for ${areas.length} detected areas. Run \`agentrc instructions --areas\` to generate.`,
9592
+ reason: `Root instructions found, but no area instructions for ${areas.length} detected areas. Run \`agentrc instructions --areas\` to generate.`,
9229
9593
  evidence: [...rootFound, ...areas.map((a) => `${a.name}: missing .instructions.md`)]
9230
9594
  };
9231
9595
  }
9232
9596
  return {
9233
9597
  status: "pass",
9234
- reason: `Root + ${fileBasedInstructions.length} file-based instruction(s) found.`,
9598
+ reason: `Root + ${fileBasedInstructions.length} area instruction(s) found.`,
9235
9599
  evidence: [...rootFound, ...fileBasedInstructions]
9236
9600
  };
9237
9601
  }
@@ -9376,7 +9740,7 @@ function buildCriteria() {
9376
9740
  if (area?.scripts?.build) {
9377
9741
  return { status: "pass" };
9378
9742
  }
9379
- const pkgPath = path8.join(context.areaPath, "package.json");
9743
+ const pkgPath = path9.join(context.areaPath, "package.json");
9380
9744
  const pkg = await readJson(pkgPath);
9381
9745
  const scripts = pkg?.scripts ?? {};
9382
9746
  const found = Boolean(scripts.build);
@@ -9401,7 +9765,7 @@ function buildCriteria() {
9401
9765
  if (area?.scripts?.test) {
9402
9766
  return { status: "pass" };
9403
9767
  }
9404
- const pkgPath = path8.join(context.areaPath, "package.json");
9768
+ const pkgPath = path9.join(context.areaPath, "package.json");
9405
9769
  const pkg = await readJson(pkgPath);
9406
9770
  const scripts = pkg?.scripts ?? {};
9407
9771
  const found = Boolean(scripts.test);
@@ -9424,7 +9788,7 @@ function buildCriteria() {
9424
9788
  return { status: "skip", reason: "No area context." };
9425
9789
  }
9426
9790
  const sanitized = sanitizeAreaName(area.name);
9427
- const instructionPath = path8.join(
9791
+ const instructionPath = path9.join(
9428
9792
  context.repoPath,
9429
9793
  ".github",
9430
9794
  "instructions",
@@ -9445,7 +9809,7 @@ function buildExtras() {
9445
9809
  id: "agents-doc",
9446
9810
  title: "AGENTS.md present",
9447
9811
  check: async (context) => {
9448
- const found = await fileExists(path8.join(context.repoPath, "AGENTS.md"));
9812
+ const found = await fileExists(path9.join(context.repoPath, "AGENTS.md"));
9449
9813
  return {
9450
9814
  status: found ? "pass" : "fail",
9451
9815
  reason: found ? void 0 : "Missing AGENTS.md to guide coding agents."
@@ -9610,11 +9974,11 @@ async function hasTypecheckConfig(repoPath) {
9610
9974
  ]);
9611
9975
  }
9612
9976
  async function hasGithubWorkflows(repoPath) {
9613
- return fileExists(path8.join(repoPath, ".github", "workflows"));
9977
+ return fileExists(path9.join(repoPath, ".github", "workflows"));
9614
9978
  }
9615
9979
  async function hasCodeowners(repoPath) {
9616
- const root = await fileExists(path8.join(repoPath, "CODEOWNERS"));
9617
- const github = await fileExists(path8.join(repoPath, ".github", "CODEOWNERS"));
9980
+ const root = await fileExists(path9.join(repoPath, "CODEOWNERS"));
9981
+ const github = await fileExists(path9.join(repoPath, ".github", "CODEOWNERS"));
9618
9982
  return root || github;
9619
9983
  }
9620
9984
  async function hasLicense(repoPath) {
@@ -9622,9 +9986,9 @@ async function hasLicense(repoPath) {
9622
9986
  return files.some((file) => file.toLowerCase().startsWith("license"));
9623
9987
  }
9624
9988
  async function hasPullRequestTemplate(repoPath) {
9625
- const direct = await fileExists(path8.join(repoPath, ".github", "PULL_REQUEST_TEMPLATE.md"));
9989
+ const direct = await fileExists(path9.join(repoPath, ".github", "PULL_REQUEST_TEMPLATE.md"));
9626
9990
  if (direct) return true;
9627
- const dir = path8.join(repoPath, ".github", "PULL_REQUEST_TEMPLATE");
9991
+ const dir = path9.join(repoPath, ".github", "PULL_REQUEST_TEMPLATE");
9628
9992
  try {
9629
9993
  const entries = await fs7.readdir(dir);
9630
9994
  return entries.some((entry) => entry.toLowerCase().endsWith(".md"));
@@ -9633,14 +9997,14 @@ async function hasPullRequestTemplate(repoPath) {
9633
9997
  }
9634
9998
  }
9635
9999
  async function hasPrecommitConfig(repoPath) {
9636
- const precommit = await fileExists(path8.join(repoPath, ".pre-commit-config.yaml"));
10000
+ const precommit = await fileExists(path9.join(repoPath, ".pre-commit-config.yaml"));
9637
10001
  if (precommit) return true;
9638
- return fileExists(path8.join(repoPath, ".husky"));
10002
+ return fileExists(path9.join(repoPath, ".husky"));
9639
10003
  }
9640
10004
  async function hasArchitectureDoc(repoPath) {
9641
10005
  const files = await safeReadDir(repoPath);
9642
10006
  if (files.some((file) => file.toLowerCase() === "architecture.md")) return true;
9643
- return fileExists(path8.join(repoPath, "docs", "architecture.md"));
10007
+ return fileExists(path9.join(repoPath, "docs", "architecture.md"));
9644
10008
  }
9645
10009
  async function hasCustomInstructions(repoPath) {
9646
10010
  const found = [];
@@ -9657,14 +10021,14 @@ async function hasCustomInstructions(repoPath) {
9657
10021
  "copilot-instructions.md"
9658
10022
  ];
9659
10023
  for (const candidate of candidates) {
9660
- if (await fileExists(path8.join(repoPath, candidate))) {
10024
+ if (await fileExists(path9.join(repoPath, candidate))) {
9661
10025
  found.push(candidate);
9662
10026
  }
9663
10027
  }
9664
10028
  return found;
9665
10029
  }
9666
10030
  async function hasFileBasedInstructions(repoPath) {
9667
- const instructionsDir = path8.join(repoPath, ".github", "instructions");
10031
+ const instructionsDir = path9.join(repoPath, ".github", "instructions");
9668
10032
  try {
9669
10033
  const entries = await fs7.readdir(instructionsDir);
9670
10034
  return entries.filter((e) => e.endsWith(".instructions.md")).map((e) => `.github/instructions/${e}`);
@@ -9692,7 +10056,7 @@ async function checkInstructionConsistency(repoPath, foundFiles) {
9692
10056
  }
9693
10057
  const realPathMap = /* @__PURE__ */ new Map();
9694
10058
  for (const file of foundFiles) {
9695
- const fullPath = path8.join(repoPath, file);
10059
+ const fullPath = path9.join(repoPath, file);
9696
10060
  try {
9697
10061
  const real = await fs7.realpath(fullPath);
9698
10062
  const group = realPathMap.get(real) ?? [];
@@ -9709,7 +10073,7 @@ async function checkInstructionConsistency(repoPath, foundFiles) {
9709
10073
  const contents = [];
9710
10074
  for (const group of groups) {
9711
10075
  try {
9712
- contents.push(await fs7.readFile(path8.join(repoPath, group[0]), "utf8"));
10076
+ contents.push(await fs7.readFile(path9.join(repoPath, group[0]), "utf8"));
9713
10077
  } catch {
9714
10078
  contents.push("");
9715
10079
  }
@@ -9728,17 +10092,17 @@ async function checkInstructionConsistency(repoPath, foundFiles) {
9728
10092
  }
9729
10093
  async function hasMcpConfig(repoPath) {
9730
10094
  const found = [];
9731
- if (await fileExists(path8.join(repoPath, ".vscode", "mcp.json"))) {
10095
+ if (await fileExists(path9.join(repoPath, ".vscode", "mcp.json"))) {
9732
10096
  found.push(".vscode/mcp.json");
9733
10097
  }
9734
- if (await fileExists(path8.join(repoPath, "mcp.json"))) {
10098
+ if (await fileExists(path9.join(repoPath, "mcp.json"))) {
9735
10099
  found.push("mcp.json");
9736
10100
  }
9737
- const settings = await readJson(path8.join(repoPath, ".vscode", "settings.json"));
10101
+ const settings = await readJson(path9.join(repoPath, ".vscode", "settings.json"));
9738
10102
  if (settings && (settings["mcp"] || settings["github.copilot.chat.mcp.enabled"])) {
9739
10103
  found.push(".vscode/settings.json (mcp section)");
9740
10104
  }
9741
- if (await fileExists(path8.join(repoPath, ".claude", "mcp.json"))) {
10105
+ if (await fileExists(path9.join(repoPath, ".claude", "mcp.json"))) {
9742
10106
  found.push(".claude/mcp.json");
9743
10107
  }
9744
10108
  return found;
@@ -9747,13 +10111,13 @@ async function hasCustomAgents(repoPath) {
9747
10111
  const found = [];
9748
10112
  const agentDirs = [".github/agents", ".copilot/agents", ".github/copilot/agents"];
9749
10113
  for (const dir of agentDirs) {
9750
- if (await fileExists(path8.join(repoPath, dir))) {
10114
+ if (await fileExists(path9.join(repoPath, dir))) {
9751
10115
  found.push(dir);
9752
10116
  }
9753
10117
  }
9754
10118
  const agentFiles = [".github/copilot-agents.yml", ".github/copilot-agents.yaml"];
9755
10119
  for (const agentFile of agentFiles) {
9756
- if (await fileExists(path8.join(repoPath, agentFile))) {
10120
+ if (await fileExists(path9.join(repoPath, agentFile))) {
9757
10121
  found.push(agentFile);
9758
10122
  }
9759
10123
  }
@@ -9768,7 +10132,7 @@ async function hasCopilotSkills(repoPath) {
9768
10132
  ".github/copilot/skills"
9769
10133
  ];
9770
10134
  for (const dir of skillDirs) {
9771
- if (await fileExists(path8.join(repoPath, dir))) {
10135
+ if (await fileExists(path9.join(repoPath, dir))) {
9772
10136
  found.push(dir);
9773
10137
  }
9774
10138
  }
@@ -9821,7 +10185,7 @@ async function processRepo(params) {
9821
10185
  } = params;
9822
10186
  try {
9823
10187
  progress?.update(`${label}: Cloning...`);
9824
- const cacheRoot = path9.join(process.cwd(), ".agentrc-cache");
10188
+ const cacheRoot = path10.join(process.cwd(), ".agentrc-cache");
9825
10189
  const repoPath = validateCachePath(cacheRoot, ...cacheParts);
9826
10190
  await ensureDir(repoPath);
9827
10191
  if (!await isGitRepo(repoPath)) {
@@ -9847,8 +10211,8 @@ async function processRepo(params) {
9847
10211
  if (!instructions.trim()) {
9848
10212
  throw new Error("Generated instructions were empty");
9849
10213
  }
9850
- const instructionsPath = path9.join(repoPath, ".github", "copilot-instructions.md");
9851
- await ensureDir(path9.dirname(instructionsPath));
10214
+ const instructionsPath = path10.join(repoPath, ".github", "copilot-instructions.md");
10215
+ await ensureDir(path10.dirname(instructionsPath));
9852
10216
  const { wrote, reason } = await safeWriteFile(instructionsPath, instructions, true);
9853
10217
  if (!wrote) {
9854
10218
  throw new Error(
@@ -9856,7 +10220,7 @@ async function processRepo(params) {
9856
10220
  );
9857
10221
  }
9858
10222
  progress?.update(`${label}: Committing...`);
9859
- await commitAll(repoPath, "chore: add copilot instructions via AgentRC");
10223
+ await commitAll(repoPath, "chore: add instructions via AgentRC");
9860
10224
  progress?.update(`${label}: Pushing...`);
9861
10225
  await pushBranch(repoPath, branch, token, provider);
9862
10226
  progress?.update(`${label}: Creating PR...`);
@@ -9892,7 +10256,7 @@ async function processGitHubRepo(options) {
9892
10256
  token,
9893
10257
  owner: repo.owner,
9894
10258
  repo: repo.name,
9895
- title: "\u{1F916} Add Copilot instructions via AgentRC",
10259
+ title: "\u{1F916} Add instructions via AgentRC",
9896
10260
  body: buildInstructionsPrBody(),
9897
10261
  head: branchName,
9898
10262
  base: repo.defaultBranch
@@ -9924,7 +10288,7 @@ async function processAzureRepo(options) {
9924
10288
  project: repo.project,
9925
10289
  repoId: repo.id,
9926
10290
  repoName: repo.name,
9927
- title: "\u{1F916} Add Copilot instructions via AgentRC",
10291
+ title: "\u{1F916} Add instructions via AgentRC",
9928
10292
  body: buildInstructionsPrBody(),
9929
10293
  sourceBranch: branchName,
9930
10294
  targetBranch: repo.defaultBranch
@@ -9985,7 +10349,7 @@ async function processBatchReadinessRepo(options) {
9985
10349
  try {
9986
10350
  progress(`Cloning ${repo.fullName}...`);
9987
10351
  const authedUrl = buildAuthedUrl(repo.cloneUrl, token, "github");
9988
- await ensureDir(path9.dirname(repoDir));
10352
+ await ensureDir(path10.dirname(repoDir));
9989
10353
  await cloneRepo(authedUrl, repoDir, { shallow: true });
9990
10354
  await setRemoteUrl(repoDir, repo.cloneUrl).catch(() => {
9991
10355
  });
@@ -11035,15 +11399,11 @@ import { render as render2 } from "ink";
11035
11399
  // src/ui/BatchReadinessTui.tsx
11036
11400
  import fs8 from "fs/promises";
11037
11401
  import os from "os";
11038
- import path10 from "path";
11402
+ import path11 from "path";
11039
11403
 
11040
11404
  // packages/core/src/services/visualReport.ts
11041
11405
  function generateVisualReport(options) {
11042
- const {
11043
- reports,
11044
- title = "AI Readiness Report",
11045
- generatedAt = (/* @__PURE__ */ new Date()).toISOString()
11046
- } = options;
11406
+ const { reports, title = "Readiness Report", generatedAt = (/* @__PURE__ */ new Date()).toISOString() } = options;
11047
11407
  const successfulReports = reports.filter((r) => !r.error);
11048
11408
  const failedReports = reports.filter((r) => r.error);
11049
11409
  const totalRepos = reports.length;
@@ -11612,7 +11972,7 @@ function generateVisualReport(options) {
11612
11972
  ` : ""}
11613
11973
 
11614
11974
  <div class="footer">
11615
- <p>Generated with <a href="https://github.com/microsoft/agentrc">AgentRC</a> &middot; AI Readiness Tool</p>
11975
+ <p>Generated with <a href="https://github.com/microsoft/agentrc">AgentRC</a> &middot; Readiness Tool</p>
11616
11976
  </div>
11617
11977
  </div>
11618
11978
  <script>
@@ -12065,7 +12425,7 @@ function BatchReadinessTui({ token, outputPath, policies }) {
12065
12425
  setStatus("processing");
12066
12426
  const selectedRepos = Array.from(selectedRepoIndices).map((i) => repos[i]);
12067
12427
  const results2 = [];
12068
- const tmpDir = path10.join(os.tmpdir(), `agentrc-batch-readiness-${Date.now()}`);
12428
+ const tmpDir = path11.join(os.tmpdir(), `agentrc-batch-readiness-${Date.now()}`);
12069
12429
  try {
12070
12430
  await ensureDir(tmpDir);
12071
12431
  for (let i = 0; i < selectedRepos.length; i++) {
@@ -12099,10 +12459,10 @@ function BatchReadinessTui({ token, outputPath, policies }) {
12099
12459
  },
12100
12460
  error: r.error
12101
12461
  })),
12102
- title: "Batch AI Readiness Report",
12462
+ title: "Batch Readiness Report",
12103
12463
  generatedAt: (/* @__PURE__ */ new Date()).toISOString()
12104
12464
  });
12105
- const finalOutputPath = outputPath ?? path10.join(process.cwd(), "batch-readiness-report.html");
12465
+ const finalOutputPath = outputPath ?? path11.join(process.cwd(), "batch-readiness-report.html");
12106
12466
  const { wrote, reason } = await safeWriteFile(finalOutputPath, html, true);
12107
12467
  if (!wrote) throw new Error(reason === "symlink" ? "Path is a symlink" : "Write failed");
12108
12468
  setStatus("complete");
@@ -12284,7 +12644,7 @@ async function batchReadinessCommand(options) {
12284
12644
  }
12285
12645
 
12286
12646
  // src/commands/eval.ts
12287
- import path12 from "path";
12647
+ import path13 from "path";
12288
12648
 
12289
12649
  // packages/core/src/services/evalScaffold.ts
12290
12650
  var EVAL_SCAFFOLD_TIMEOUT_MS = 6e5;
@@ -12306,7 +12666,7 @@ async function generateEvalScaffold(options) {
12306
12666
  streaming: true,
12307
12667
  workingDirectory: repoPath,
12308
12668
  systemMessage: {
12309
- content: "You are an expert codebase analyst specializing in deep architectural analysis. Generate challenging, cross-cutting eval cases for this repository that require synthesizing information from multiple files and tracing logic across layers. Avoid trivial questions answerable from a single file read or grep. Use tools (glob, view, grep) extensively to inspect the codebase. Output ONLY JSON with keys: instructionFile, cases (array of {id,prompt,expectation})."
12669
+ content: "Generate challenging implementation planning tasks for this repository. Each task should require cross-cutting changes across multiple files. Tasks can range from specific feature additions to broader refactoring plans. Avoid trivial tasks or pure Q&A questions. Output ONLY JSON with keys: instructionFile, cases (array of {id,prompt,expectation})."
12310
12670
  },
12311
12671
  infiniteSessions: { enabled: false }
12312
12672
  });
@@ -12348,32 +12708,37 @@ async function generateEvalScaffold(options) {
12348
12708
  'For areas with a workspace (workingDirectory), include a "workingDirectory" field set to that workspace path so evals run scoped to the correct folder.'
12349
12709
  ].join("\n") : "";
12350
12710
  const prompt = [
12351
- `Analyze this repository and generate ${count} eval cases.`,
12711
+ `Analyze this repository and generate ${count} implementation planning tasks.`,
12712
+ "",
12713
+ "Each task should represent a REAL developer request \u2014 the kind of thing someone would actually type into a coding agent.",
12714
+ "Tasks should require understanding MULTIPLE files and cross-cutting concerns.",
12352
12715
  "",
12353
- "IMPORTANT: Generate HARD eval cases that require deep, cross-cutting understanding of the codebase.",
12354
- "Each case should require synthesizing information from MULTIPLE files or tracing logic across several layers.",
12355
- "Do NOT generate simple questions that can be answered by reading a single file or running a single grep.",
12716
+ "PROMPT RULES (the prompt field is what the eval agent receives):",
12717
+ "- Write prompts as a developer would naturally phrase them \u2014 short, direct requests.",
12718
+ "- Do NOT describe expected output format or what the response should include.",
12719
+ "- Do NOT add meta-instructions like 'The output should show...' or 'Include in your plan...' or 'Your plan should cover...'",
12720
+ "- Do NOT embed implementation details in the prompt (pillar names, levels, specific output formats) \u2014 those belong in the expectation.",
12721
+ "- Good: 'Add a --dry-run flag to the generate command'",
12722
+ "- Good: 'Add a readiness criterion for CI/CD detection'",
12723
+ "- Bad: 'Add a --dry-run flag that previews what files would be created or modified. The output should show file paths and indicate whether each would be created, updated, or skipped.'",
12724
+ "- Bad: 'Add a readiness criterion that checks for CI/CD and classifies it under a new deployment pillar at level 3'",
12356
12725
  "",
12357
- "Good eval case examples (adapt to this repo):",
12358
- "- Questions about how data flows end-to-end through multiple modules (e.g., 'Trace what happens when X is called \u2014 which services, transforms, and side effects are involved?')",
12359
- "- Questions about implicit conventions or patterns that span many files (e.g., 'What error-handling pattern is used across the service layer, and where does it deviate?')",
12360
- "- Questions requiring understanding of runtime behavior not obvious from static code (e.g., 'What is the order of initialization and what would break if module X loaded before Y?')",
12361
- "- Questions about non-obvious interactions between components (e.g., 'How does changing config option X affect the behavior of feature Y?')",
12362
- "- Questions about edge cases or failure modes that require reading implementation details across files",
12363
- "- Questions that require understanding the type system, generics, or shared interfaces across module boundaries",
12726
+ "EXPECTATION RULES (the expectation field is checked by an LLM judge \u2014 be extremely specific):",
12727
+ "- MUST cite exact file paths (e.g., 'packages/core/src/services/generator.ts')",
12728
+ "- MUST cite exact function and type names (e.g., 'generateCopilotInstructions()', 'CommandResult<T>')",
12729
+ "- MUST cite specific CLI commands for verification (e.g., 'npm run typecheck', 'npm test')",
12730
+ "- MUST name the specific test file to update (e.g., 'src/services/__tests__/generator.test.ts')",
12731
+ "- Generic expectations like 'identify the generator service' are USELESS \u2014 name the actual function and file.",
12364
12732
  "",
12365
- "Bad eval case examples (avoid these):",
12366
- "- 'What does this project do?' (answered by README alone)",
12367
- "- 'How do I build/test?' (answered by package.json alone)",
12368
- "- 'What is the entrypoint?' (answered by a single file)",
12369
- "- Any question answerable by reading one file or searching for one keyword",
12733
+ "Bad task examples (avoid these):",
12734
+ "- 'What does this project do?' (pure Q&A, not a planning task)",
12735
+ "- 'Explain the architecture' (no implementation expected)",
12736
+ "- Tasks with prescriptive output instructions baked into the prompt",
12370
12737
  "",
12371
- "Use tools extensively to inspect the codebase \u2014 read multiple files, trace imports, follow call chains.",
12372
- "If this is a monorepo (npm/pnpm/yarn workspaces, Cargo workspace, Go workspace, .NET solution, Gradle/Maven multi-module), generate cases that involve cross-app dependencies, shared libraries, and how changes in one app affect others.",
12373
- "Ensure cases cover cross-cutting concerns: data flow, error propagation, configuration impact, implicit coupling, architectural invariants.",
12374
- "Include a systemMessage that keeps answers scoped to this repository (avoid generic Copilot CLI details unless asked).",
12738
+ "Do NOT generate a systemMessage \u2014 the default is used automatically.",
12739
+ "If this is a monorepo, generate tasks that involve cross-app dependencies and shared libraries.",
12375
12740
  "Return JSON ONLY (no markdown, no commentary) in this schema:",
12376
- '{\n "instructionFile": ".github/copilot-instructions.md",\n "systemMessage": "...",\n "cases": [\n {"id": "case-1", "prompt": "...", "expectation": "...", "area": "optional-area-name"}\n ]\n}',
12741
+ options.areas?.length ? '{\n "instructionFile": ".github/copilot-instructions.md",\n "cases": [\n {"id": "case-1", "prompt": "...", "expectation": "...", "area": "optional-area-name"}\n ]\n}' : '{\n "instructionFile": ".github/copilot-instructions.md",\n "cases": [\n {"id": "case-1", "prompt": "...", "expectation": "..."}\n ]\n}',
12377
12742
  areaContext
12378
12743
  ].join("\n");
12379
12744
  progress("Analyzing codebase...");
@@ -12419,7 +12784,8 @@ async function generateEvalScaffold(options) {
12419
12784
  }
12420
12785
  throw error;
12421
12786
  }
12422
- const normalized = normalizeEvalConfig(parsed, count);
12787
+ const hasAreas = Boolean(options.areas?.length);
12788
+ const normalized = normalizeEvalConfig(parsed, count, hasAreas);
12423
12789
  return normalized;
12424
12790
  } finally {
12425
12791
  await client.stop();
@@ -12443,41 +12809,47 @@ function parseEvalConfig(raw) {
12443
12809
  }
12444
12810
  return parsed;
12445
12811
  }
12446
- function normalizeEvalConfig(parsed, count) {
12812
+ function normalizeEvalConfig(parsed, count, hasAreas = true) {
12447
12813
  const cases = (parsed.cases ?? []).slice(0, count).map((entry, index) => {
12448
12814
  const id = typeof entry.id === "string" && entry.id.trim() ? entry.id : `case-${index + 1}`;
12449
12815
  return {
12450
12816
  id,
12451
12817
  prompt: String(entry.prompt ?? "").trim(),
12452
- expectation: String(entry.expectation ?? "").trim(),
12453
- area: typeof entry.area === "string" && entry.area.trim() ? entry.area.trim() : void 0,
12818
+ expectation: normalizeExpectation(entry.expectation),
12819
+ ...hasAreas && typeof entry.area === "string" && entry.area.trim() ? { area: entry.area.trim() } : {},
12454
12820
  workingDirectory: typeof entry.workingDirectory === "string" && entry.workingDirectory.trim() ? entry.workingDirectory.trim() : void 0
12455
12821
  };
12456
12822
  });
12457
12823
  if (!cases.length) {
12458
12824
  throw new Error("Eval scaffold JSON did not include any usable cases.");
12459
12825
  }
12460
- const defaultSystemMessage = "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.";
12461
12826
  return {
12462
12827
  instructionFile: parsed.instructionFile ?? ".github/copilot-instructions.md",
12463
- systemMessage: parsed.systemMessage ?? defaultSystemMessage,
12828
+ ...parsed.systemMessage ? { systemMessage: parsed.systemMessage } : {},
12464
12829
  cases
12465
12830
  };
12466
12831
  }
12832
+ function normalizeExpectation(value) {
12833
+ if (Array.isArray(value)) {
12834
+ const items = value.map((s) => String(s).trim()).filter(Boolean);
12835
+ return items.length === 1 ? items[0] : items;
12836
+ }
12837
+ return String(value ?? "").trim();
12838
+ }
12467
12839
 
12468
12840
  // packages/core/src/services/evaluator.ts
12469
12841
  import fs9 from "fs/promises";
12470
- import path11 from "path";
12471
- 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.";
12842
+ import path12 from "path";
12843
+ var DEFAULT_SYSTEM_MESSAGE = "Research using read-only tools and make a plan for the given task, including concrete implementation steps and verification steps.";
12472
12844
  async function runEval(options) {
12473
12845
  const config = await loadConfig(options.configPath);
12474
12846
  const instructionFile = config.instructionFile ?? ".github/copilot-instructions.md";
12475
- const instructionPath = path11.resolve(options.repoPath, instructionFile);
12847
+ const instructionPath = path12.resolve(options.repoPath, instructionFile);
12476
12848
  const instructionText = await readOptionalFile(instructionPath);
12477
12849
  const baseSystemMessage = config.systemMessage ?? DEFAULT_SYSTEM_MESSAGE;
12478
12850
  const progress = options.onProgress ?? (() => {
12479
12851
  });
12480
- const defaultOutputPath = path11.resolve(
12852
+ const defaultOutputPath = path12.resolve(
12481
12853
  options.repoPath,
12482
12854
  ".agentrc",
12483
12855
  "evals",
@@ -12497,9 +12869,9 @@ async function runEval(options) {
12497
12869
  const caseStartedAt = Date.now();
12498
12870
  let caseWorkingDir;
12499
12871
  if (testCase.workingDirectory) {
12500
- const resolved = path11.resolve(options.repoPath, testCase.workingDirectory);
12501
- const root = path11.resolve(options.repoPath);
12502
- if (resolved !== root && !resolved.startsWith(root + path11.sep)) {
12872
+ const resolved = path12.resolve(options.repoPath, testCase.workingDirectory);
12873
+ const root = path12.resolve(options.repoPath);
12874
+ if (resolved !== root && !resolved.startsWith(root + path12.sep)) {
12503
12875
  throw new Error(
12504
12876
  `Invalid workingDirectory "${testCase.workingDirectory}": escapes repo boundary`
12505
12877
  );
@@ -12571,7 +12943,7 @@ async function runEval(options) {
12571
12943
  };
12572
12944
  let viewerPath;
12573
12945
  if (outputPath) {
12574
- await fs9.mkdir(path11.dirname(outputPath), { recursive: true });
12946
+ await fs9.mkdir(path12.dirname(outputPath), { recursive: true });
12575
12947
  await safeWriteFile(outputPath, JSON.stringify(output, null, 2), true);
12576
12948
  viewerPath = buildViewerPath(outputPath);
12577
12949
  await safeWriteFile(viewerPath, buildTrajectoryViewerHtml(output), true);
@@ -12632,10 +13004,11 @@ async function judge(client, options) {
12632
13004
  if (delta) content += delta;
12633
13005
  }
12634
13006
  });
13007
+ const expectationText = Array.isArray(options.expectation) ? options.expectation.join("\n") : options.expectation;
12635
13008
  const prompt = [
12636
13009
  "Evaluate which response best matches the expectation.",
12637
13010
  "",
12638
- `Expectation: ${options.expectation}`,
13011
+ `Expectation: ${expectationText}`,
12639
13012
  "",
12640
13013
  "Response A (without custom instructions):",
12641
13014
  options.withoutInstructions,
@@ -12679,7 +13052,7 @@ function parseJudge(content) {
12679
13052
  }
12680
13053
  async function loadConfig(configPath) {
12681
13054
  const raw = await fs9.readFile(configPath, "utf8");
12682
- const parsed = JSON.parse(raw);
13055
+ const parsed = JSON.parse(stripJsonComments(raw));
12683
13056
  if (!parsed || !Array.isArray(parsed.cases)) {
12684
13057
  throw new Error("Eval config must have a 'cases' array.");
12685
13058
  }
@@ -12885,7 +13258,7 @@ function formatTokenUsage(usage) {
12885
13258
  function resolveOutputPath(repoPath, override, configValue) {
12886
13259
  const chosen = override ?? configValue;
12887
13260
  if (!chosen) return void 0;
12888
- return path11.isAbsolute(chosen) ? chosen : path11.resolve(repoPath, chosen);
13261
+ return path12.isAbsolute(chosen) ? chosen : path12.resolve(repoPath, chosen);
12889
13262
  }
12890
13263
  function buildViewerPath(outputPath) {
12891
13264
  if (outputPath.endsWith(".json")) {
@@ -13240,7 +13613,7 @@ function sanitizeValue(value, depth) {
13240
13613
 
13241
13614
  // src/commands/eval.ts
13242
13615
  async function evalCommand(configPathArg, options) {
13243
- const repoPath = path12.resolve(options.repo ?? process.cwd());
13616
+ const repoPath = path13.resolve(options.repo ?? process.cwd());
13244
13617
  if (options.listModels) {
13245
13618
  try {
13246
13619
  const models = await listCopilotModels();
@@ -13276,7 +13649,7 @@ async function evalCommand(configPathArg, options) {
13276
13649
  return;
13277
13650
  }
13278
13651
  if (options.init) {
13279
- const outputPath = path12.join(repoPath, "agentrc.eval.json");
13652
+ const outputPath = path13.join(repoPath, "agentrc.eval.json");
13280
13653
  const desiredCount = Math.max(1, Number.parseInt(options.count ?? "5", 10) || 5);
13281
13654
  try {
13282
13655
  const progress = createProgressReporter(!shouldLog(options));
@@ -13318,7 +13691,7 @@ async function evalCommand(configPathArg, options) {
13318
13691
  }
13319
13692
  return;
13320
13693
  }
13321
- const configPath = path12.resolve(configPathArg ?? path12.join(repoPath, "agentrc.eval.json"));
13694
+ const configPath = path13.resolve(configPathArg ?? path13.join(repoPath, "agentrc.eval.json"));
13322
13695
  try {
13323
13696
  const progress = createProgressReporter(!shouldLog(options));
13324
13697
  const { summary, results, viewerPath } = await runEval({
@@ -13365,32 +13738,43 @@ async function evalCommand(configPathArg, options) {
13365
13738
  }
13366
13739
 
13367
13740
  // src/commands/generate.ts
13368
- import path15 from "path";
13741
+ import path16 from "path";
13369
13742
 
13370
13743
  // packages/core/src/services/generator.ts
13371
- import path13 from "path";
13744
+ import path14 from "path";
13745
+ async function writeOrPreview(filePath, content, opts) {
13746
+ const relPath = path14.relative(process.cwd(), filePath);
13747
+ if (opts.dryRun) {
13748
+ const wouldWrite = await canSafeWrite(filePath, opts.force);
13749
+ return {
13750
+ path: relPath,
13751
+ action: wouldWrite ? "wrote" : "skipped",
13752
+ bytes: Buffer.byteLength(content, "utf8")
13753
+ };
13754
+ }
13755
+ await ensureDir(path14.dirname(filePath));
13756
+ const { wrote } = await safeWriteFile(filePath, content, opts.force);
13757
+ return { path: relPath, action: wrote ? "wrote" : "skipped" };
13758
+ }
13372
13759
  async function generateConfigs(options) {
13373
- const { repoPath, analysis, selections, force } = options;
13760
+ const { repoPath, analysis, selections, force, dryRun } = options;
13374
13761
  const files = [];
13375
13762
  if (selections.includes("mcp")) {
13376
- const filePath = path13.join(repoPath, ".vscode", "mcp.json");
13377
- await ensureDir(path13.dirname(filePath));
13378
- const content = renderMcp();
13379
- const { wrote } = await safeWriteFile(filePath, content, force);
13380
- files.push({
13381
- path: path13.relative(process.cwd(), filePath),
13382
- action: wrote ? "wrote" : "skipped"
13383
- });
13763
+ files.push(
13764
+ await writeOrPreview(path14.join(repoPath, ".vscode", "mcp.json"), renderMcp(), {
13765
+ dryRun,
13766
+ force
13767
+ })
13768
+ );
13384
13769
  }
13385
13770
  if (selections.includes("vscode")) {
13386
- const filePath = path13.join(repoPath, ".vscode", "settings.json");
13387
- await ensureDir(path13.dirname(filePath));
13388
- const content = renderVscodeSettings(analysis);
13389
- const { wrote } = await safeWriteFile(filePath, content, force);
13390
- files.push({
13391
- path: path13.relative(process.cwd(), filePath),
13392
- action: wrote ? "wrote" : "skipped"
13393
- });
13771
+ files.push(
13772
+ await writeOrPreview(
13773
+ path14.join(repoPath, ".vscode", "settings.json"),
13774
+ renderVscodeSettings(analysis),
13775
+ { dryRun, force }
13776
+ )
13777
+ );
13394
13778
  }
13395
13779
  return { files };
13396
13780
  }
@@ -13439,16 +13823,16 @@ function renderVscodeSettings(analysis) {
13439
13823
  }
13440
13824
 
13441
13825
  // src/commands/instructions.ts
13442
- import path14 from "path";
13826
+ import path15 from "path";
13443
13827
  function skipReason(action) {
13444
13828
  if (action === "symlink") return "symlink";
13445
13829
  if (action === "empty") return "empty content";
13446
13830
  return "exists, use --force";
13447
13831
  }
13448
13832
  async function instructionsCommand(options) {
13449
- const repoPath = path14.resolve(options.repo ?? process.cwd());
13450
- const outputPath = path14.resolve(
13451
- options.output ?? path14.join(repoPath, ".github", "copilot-instructions.md")
13833
+ const repoPath = path15.resolve(options.repo ?? process.cwd());
13834
+ const outputPath = path15.resolve(
13835
+ options.output ?? path15.join(repoPath, ".github", "copilot-instructions.md")
13452
13836
  );
13453
13837
  const progress = createProgressReporter(!shouldLog(options));
13454
13838
  const wantAreas = options.areas || options.areasOnly || options.area;
@@ -13471,6 +13855,7 @@ async function instructionsCommand(options) {
13471
13855
  return;
13472
13856
  }
13473
13857
  try {
13858
+ const dryRunFiles = [];
13474
13859
  if (!options.areasOnly && !options.area) {
13475
13860
  if (strategy === "nested") {
13476
13861
  try {
@@ -13482,25 +13867,57 @@ async function instructionsCommand(options) {
13482
13867
  detailDir,
13483
13868
  claudeMd
13484
13869
  });
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)})`);
13870
+ if (options.dryRun) {
13871
+ const dryFiles = [
13872
+ { path: nestedResult.hub.relativePath, content: nestedResult.hub.content },
13873
+ ...nestedResult.details.map((d) => ({ path: d.relativePath, content: d.content })),
13874
+ ...nestedResult.claudeMd ? [
13875
+ {
13876
+ path: nestedResult.claudeMd.relativePath,
13877
+ content: nestedResult.claudeMd.content
13878
+ }
13879
+ ] : []
13880
+ ];
13881
+ for (const file of dryFiles) {
13882
+ const relPath = path15.relative(process.cwd(), path15.join(repoPath, file.path));
13883
+ if (shouldLog(options)) {
13884
+ progress.update(
13885
+ `[dry-run] Would write ${relPath} (${Buffer.byteLength(file.content, "utf8")} bytes)`
13886
+ );
13887
+ }
13888
+ }
13889
+ if (options.json) {
13890
+ dryRunFiles.push(
13891
+ ...dryFiles.map((f) => ({
13892
+ path: f.path,
13893
+ bytes: Buffer.byteLength(f.content, "utf8")
13894
+ }))
13895
+ );
13896
+ }
13897
+ for (const warning of nestedResult.warnings) {
13898
+ if (shouldLog(options)) progress.update(`Warning: ${warning}`);
13899
+ }
13900
+ } else {
13901
+ const actions = await writeNestedInstructions(repoPath, nestedResult, options.force);
13902
+ for (const action of actions) {
13903
+ const relPath = path15.relative(process.cwd(), action.path);
13904
+ if (action.action === "wrote") {
13905
+ if (shouldLog(options)) progress.succeed(`Wrote ${relPath}`);
13906
+ } else if (shouldLog(options)) {
13907
+ progress.update(`Skipped ${relPath} (${skipReason(action.action)})`);
13908
+ }
13909
+ }
13910
+ for (const warning of nestedResult.warnings) {
13911
+ if (shouldLog(options)) progress.update(`Warning: ${warning}`);
13912
+ }
13913
+ if (options.json) {
13914
+ const result = {
13915
+ ok: true,
13916
+ status: "success",
13917
+ data: { files: actions }
13918
+ };
13919
+ outputResult(result, true);
13492
13920
  }
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
13921
  }
13505
13922
  } catch (error) {
13506
13923
  const msg = "Failed to generate nested instructions. " + (error instanceof Error ? error.message : String(error));
@@ -13525,36 +13942,48 @@ async function instructionsCommand(options) {
13525
13942
  return;
13526
13943
  }
13527
13944
  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)";
13945
+ if (options.dryRun) {
13946
+ const relPath = path15.relative(repoPath, outputPath);
13947
+ const displayPath = path15.relative(process.cwd(), outputPath);
13948
+ const byteCount = Buffer.byteLength(content, "utf8");
13949
+ if (shouldLog(options)) {
13950
+ progress.update(`[dry-run] Would write ${displayPath} (${byteCount} bytes)`);
13951
+ }
13537
13952
  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}`);
13953
+ dryRunFiles.push({ path: relPath, bytes: byteCount });
13546
13954
  }
13547
13955
  } 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)}`);
13956
+ await ensureDir(path15.dirname(outputPath));
13957
+ const { wrote, reason } = await safeWriteFile(
13958
+ outputPath,
13959
+ content,
13960
+ Boolean(options.force)
13961
+ );
13962
+ if (!wrote) {
13963
+ const relPath = path15.relative(process.cwd(), outputPath);
13964
+ const why = reason === "symlink" ? "path is a symlink" : "file exists (use --force)";
13965
+ if (options.json) {
13966
+ const result = {
13967
+ ok: true,
13968
+ status: "noop",
13969
+ data: { outputPath, skipped: true, reason: why }
13970
+ };
13971
+ outputResult(result, true);
13972
+ } else if (shouldLog(options)) {
13973
+ progress.update(`Skipped ${relPath}: ${why}`);
13974
+ }
13975
+ } else {
13976
+ const byteCount = Buffer.byteLength(content, "utf8");
13977
+ if (options.json) {
13978
+ const result = {
13979
+ ok: true,
13980
+ status: "success",
13981
+ data: { outputPath, model: options.model ?? "default", byteCount }
13982
+ };
13983
+ outputResult(result, true);
13984
+ } else if (shouldLog(options)) {
13985
+ progress.succeed(`Updated ${path15.relative(process.cwd(), outputPath)}`);
13986
+ }
13558
13987
  }
13559
13988
  }
13560
13989
  }
@@ -13588,7 +14017,7 @@ async function instructionsCommand(options) {
13588
14017
  return;
13589
14018
  }
13590
14019
  if (shouldLog(options)) {
13591
- progress.update(`Generating file-based instructions for ${targetAreas.length} area(s)...`);
14020
+ progress.update(`Generating instructions for ${targetAreas.length} area(s)...`);
13592
14021
  }
13593
14022
  for (const area of targetAreas) {
13594
14023
  try {
@@ -13608,17 +14037,46 @@ async function instructionsCommand(options) {
13608
14037
  detailDir,
13609
14038
  claudeMd
13610
14039
  });
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)})`);
14040
+ if (options.dryRun) {
14041
+ const dryFiles = [
14042
+ { path: nestedResult.hub.relativePath, content: nestedResult.hub.content },
14043
+ ...nestedResult.details.map((d) => ({ path: d.relativePath, content: d.content })),
14044
+ ...nestedResult.claudeMd ? [
14045
+ {
14046
+ path: nestedResult.claudeMd.relativePath,
14047
+ content: nestedResult.claudeMd.content
14048
+ }
14049
+ ] : []
14050
+ ];
14051
+ for (const file of dryFiles) {
14052
+ const relPath = path15.relative(process.cwd(), path15.join(repoPath, file.path));
14053
+ if (shouldLog(options)) {
14054
+ progress.update(
14055
+ `[dry-run] Would write ${relPath} (${Buffer.byteLength(file.content, "utf8")} bytes)`
14056
+ );
14057
+ }
14058
+ }
14059
+ if (options.json) {
14060
+ dryRunFiles.push(
14061
+ ...dryFiles.map((f) => ({
14062
+ path: f.path,
14063
+ bytes: Buffer.byteLength(f.content, "utf8")
14064
+ }))
14065
+ );
14066
+ }
14067
+ } else {
14068
+ const actions = await writeNestedInstructions(repoPath, nestedResult, options.force);
14069
+ for (const action of actions) {
14070
+ const relPath = path15.relative(process.cwd(), action.path);
14071
+ if (action.action === "wrote") {
14072
+ if (shouldLog(options)) progress.succeed(`Wrote ${relPath}`);
14073
+ } else if (shouldLog(options)) {
14074
+ progress.update(`Skipped ${relPath} (${skipReason(action.action)})`);
14075
+ }
14076
+ }
14077
+ for (const warning of nestedResult.warnings) {
14078
+ if (shouldLog(options)) progress.update(`Warning: ${warning}`);
13618
14079
  }
13619
- }
13620
- for (const warning of nestedResult.warnings) {
13621
- if (shouldLog(options)) progress.update(`Warning: ${warning}`);
13622
14080
  }
13623
14081
  } else {
13624
14082
  const body = await generateAreaInstructions({
@@ -13633,21 +14091,37 @@ async function instructionsCommand(options) {
13633
14091
  }
13634
14092
  continue;
13635
14093
  }
13636
- const result = await writeAreaInstruction(repoPath, area, body, options.force);
13637
- if (result.status === "skipped") {
14094
+ if (options.dryRun) {
13638
14095
  if (shouldLog(options)) {
13639
- progress.update(`Skipped "${area.name}" \u2014 file exists (use --force to overwrite).`);
14096
+ progress.update(
14097
+ `[dry-run] Would write area "${area.name}" (${Buffer.byteLength(body, "utf8")} bytes)`
14098
+ );
14099
+ }
14100
+ if (options.json) {
14101
+ dryRunFiles.push({
14102
+ path: path15.relative(repoPath, areaInstructionPath(repoPath, area)),
14103
+ bytes: Buffer.byteLength(body, "utf8")
14104
+ });
14105
+ }
14106
+ } else {
14107
+ const result = await writeAreaInstruction(repoPath, area, body, options.force);
14108
+ if (result.status === "skipped") {
14109
+ if (shouldLog(options)) {
14110
+ progress.update(
14111
+ `Skipped "${area.name}" \u2014 file exists (use --force to overwrite).`
14112
+ );
14113
+ }
14114
+ continue;
14115
+ }
14116
+ if (result.status === "symlink") {
14117
+ if (shouldLog(options)) {
14118
+ progress.update(`Skipped "${area.name}" \u2014 path is a symlink.`);
14119
+ }
14120
+ continue;
13640
14121
  }
13641
- continue;
13642
- }
13643
- if (result.status === "symlink") {
13644
14122
  if (shouldLog(options)) {
13645
- progress.update(`Skipped "${area.name}" \u2014 path is a symlink.`);
14123
+ progress.succeed(`Wrote ${path15.relative(process.cwd(), result.filePath)}`);
13646
14124
  }
13647
- continue;
13648
- }
13649
- if (shouldLog(options)) {
13650
- progress.succeed(`Wrote ${path14.relative(process.cwd(), result.filePath)}`);
13651
14125
  }
13652
14126
  }
13653
14127
  } catch (error) {
@@ -13659,6 +14133,12 @@ async function instructionsCommand(options) {
13659
14133
  }
13660
14134
  }
13661
14135
  }
14136
+ if (options.dryRun && options.json) {
14137
+ outputResult(
14138
+ { ok: true, status: "noop", data: { dryRun: true, files: dryRunFiles } },
14139
+ true
14140
+ );
14141
+ }
13662
14142
  if (!wantAreas && shouldLog(options) && !options.json) {
13663
14143
  process.stderr.write("\nNext steps:\n");
13664
14144
  process.stderr.write(" agentrc eval --init Scaffold evaluation test cases\n");
@@ -13675,9 +14155,21 @@ async function instructionsCommand(options) {
13675
14155
 
13676
14156
  // src/commands/generate.ts
13677
14157
  async function generateCommand(type, repoPathArg, options) {
13678
- const repoPath = path15.resolve(repoPathArg ?? process.cwd());
14158
+ const repoPath = path16.resolve(repoPathArg ?? process.cwd());
13679
14159
  if (type === "instructions" || type === "agents") {
13680
- const output = type === "agents" ? path15.join(repoPath, "AGENTS.md") : void 0;
14160
+ if (!options.quiet) {
14161
+ process.stderr.write(
14162
+ `\u26A0 \`generate ${type}\` is deprecated \u2014 use \`agentrc instructions\` directly.
14163
+ `
14164
+ );
14165
+ }
14166
+ if (options.perApp && !options.quiet) {
14167
+ process.stderr.write(
14168
+ `\u26A0 --per-app is deprecated \u2014 use \`agentrc instructions --areas\` instead.
14169
+ `
14170
+ );
14171
+ }
14172
+ const output = type === "agents" ? path16.join(repoPath, "AGENTS.md") : void 0;
13681
14173
  await instructionsCommand({
13682
14174
  repo: repoPath,
13683
14175
  output,
@@ -13686,7 +14178,8 @@ async function generateCommand(type, repoPathArg, options) {
13686
14178
  json: options.json,
13687
14179
  quiet: options.quiet,
13688
14180
  areas: options.perApp,
13689
- strategy: options.strategy
14181
+ strategy: options.strategy,
14182
+ dryRun: options.dryRun
13690
14183
  });
13691
14184
  return;
13692
14185
  }
@@ -13707,7 +14200,8 @@ async function generateCommand(type, repoPathArg, options) {
13707
14200
  repoPath,
13708
14201
  analysis,
13709
14202
  selections,
13710
- force: Boolean(options.force)
14203
+ force: Boolean(options.force),
14204
+ dryRun: Boolean(options.dryRun)
13711
14205
  });
13712
14206
  } catch (error) {
13713
14207
  outputError(
@@ -13721,14 +14215,16 @@ async function generateCommand(type, repoPathArg, options) {
13721
14215
  const result = {
13722
14216
  ok,
13723
14217
  status,
13724
- data: { type, files: genResult.files }
14218
+ data: { type, files: genResult.files, dryRun: options.dryRun }
13725
14219
  };
13726
14220
  outputResult(result, true);
13727
14221
  if (!ok) process.exitCode = 1;
13728
14222
  } else {
13729
14223
  for (const file of genResult.files) {
13730
14224
  if (shouldLog(options)) {
13731
- process.stderr.write(`${file.action === "wrote" ? "Wrote" : "Skipped"} ${file.path}
14225
+ const prefix = options.dryRun ? file.action === "wrote" ? "[dry-run] Would write" : "[dry-run] Would skip" : file.action === "wrote" ? "Wrote" : "Skipped";
14226
+ const suffix = options.dryRun && file.bytes !== void 0 ? ` (${file.bytes} bytes)` : "";
14227
+ process.stderr.write(`${prefix} ${file.path}${suffix}
13732
14228
  `);
13733
14229
  }
13734
14230
  }
@@ -13739,10 +14235,35 @@ async function generateCommand(type, repoPathArg, options) {
13739
14235
  }
13740
14236
 
13741
14237
  // src/commands/init.ts
13742
- import path16 from "path";
14238
+ import path18 from "path";
14239
+
14240
+ // packages/core/src/services/configScaffold.ts
14241
+ import path17 from "path";
14242
+ async function scaffoldAgentrcConfig(repoPath, areas, force = false) {
14243
+ const configPath = path17.join(repoPath, "agentrc.config.json");
14244
+ const workspaces = areas.length > 0 ? await detectWorkspaces(repoPath, areas) : [];
14245
+ const workspacePaths = workspaces.map((ws) => ws.path + "/");
14246
+ const standaloneAreas = areas.filter((a) => {
14247
+ if (!a.path) return true;
14248
+ const rel = path17.relative(repoPath, a.path).replace(/\\/gu, "/");
14249
+ return !workspacePaths.some((prefix) => rel.startsWith(prefix));
14250
+ }).map((a) => ({
14251
+ name: a.name,
14252
+ applyTo: a.applyTo,
14253
+ ...a.description ? { description: a.description } : {}
14254
+ }));
14255
+ const agentrcConfig = {};
14256
+ if (workspaces.length > 0) agentrcConfig.workspaces = workspaces;
14257
+ if (standaloneAreas.length > 0) agentrcConfig.areas = standaloneAreas;
14258
+ const configContent = JSON.stringify(agentrcConfig, null, 2) + "\n";
14259
+ const { wrote } = await safeWriteFile(configPath, configContent, force);
14260
+ return { wrote, configPath };
14261
+ }
14262
+
14263
+ // src/commands/init.ts
13743
14264
  import { checkbox, select } from "@inquirer/prompts";
13744
14265
  async function initCommand(repoPathArg, options) {
13745
- let repoPath = path16.resolve(repoPathArg ?? process.cwd());
14266
+ let repoPath = path18.resolve(repoPathArg ?? process.cwd());
13746
14267
  const provider = options.provider ?? (options.github ? "github" : void 0);
13747
14268
  if (provider && provider !== "github" && provider !== "azure") {
13748
14269
  outputError("Invalid provider. Use github or azure.", Boolean(options.json));
@@ -13780,7 +14301,7 @@ async function initCommand(repoPathArg, options) {
13780
14301
  value: repo
13781
14302
  }))
13782
14303
  });
13783
- const cacheRoot = path16.join(process.cwd(), ".agentrc-cache");
14304
+ const cacheRoot = path18.join(process.cwd(), ".agentrc-cache");
13784
14305
  repoPath = validateCachePath(cacheRoot, selection.owner, selection.name);
13785
14306
  await ensureDir(repoPath);
13786
14307
  const hasGit = await isGitRepo(repoPath);
@@ -13833,7 +14354,7 @@ async function initCommand(repoPathArg, options) {
13833
14354
  value: repo
13834
14355
  }))
13835
14356
  });
13836
- const cacheRoot = path16.join(process.cwd(), ".agentrc-cache");
14357
+ const cacheRoot = path18.join(process.cwd(), ".agentrc-cache");
13837
14358
  repoPath = validateCachePath(
13838
14359
  cacheRoot,
13839
14360
  orgSelection.name,
@@ -13872,17 +14393,17 @@ async function initCommand(repoPathArg, options) {
13872
14393
  });
13873
14394
  const allFiles = [];
13874
14395
  if (selections.includes("instructions")) {
13875
- const outputPath = path16.join(repoPath, ".github", "copilot-instructions.md");
13876
- await ensureDir(path16.dirname(outputPath));
14396
+ const outputPath = path18.join(repoPath, ".github", "copilot-instructions.md");
14397
+ await ensureDir(path18.dirname(outputPath));
13877
14398
  try {
13878
14399
  const content = await generateCopilotInstructions({ repoPath, model: options.model });
13879
14400
  const { wrote } = await safeWriteFile(outputPath, content, Boolean(options.force));
13880
14401
  allFiles.push({
13881
- path: path16.relative(process.cwd(), outputPath),
14402
+ path: path18.relative(process.cwd(), outputPath),
13882
14403
  action: wrote ? "wrote" : "skipped"
13883
14404
  });
13884
14405
  if (shouldLog(options)) {
13885
- const rel = path16.relative(process.cwd(), outputPath);
14406
+ const rel = path18.relative(process.cwd(), outputPath);
13886
14407
  process.stderr.write((wrote ? `Wrote ${rel}` : `Skipped ${rel} (exists)`) + "\n");
13887
14408
  }
13888
14409
  } catch (error) {
@@ -13909,30 +14430,16 @@ async function initCommand(repoPathArg, options) {
13909
14430
  return;
13910
14431
  }
13911
14432
  allFiles.push(...genResult.files);
13912
- if (analysis.areas && analysis.areas.length > 0) {
13913
- const configPath = path16.join(repoPath, "agentrc.config.json");
13914
- const workspaces = await detectWorkspaces(repoPath, analysis.areas);
13915
- const workspacePaths = workspaces.map((ws) => ws.path + "/");
13916
- const standaloneAreas = analysis.areas.filter((a) => {
13917
- if (!a.path) return true;
13918
- const rel = path16.relative(repoPath, a.path).replace(/\\/gu, "/");
13919
- return !workspacePaths.some((prefix) => rel.startsWith(prefix));
13920
- }).map((a) => ({
13921
- name: a.name,
13922
- applyTo: a.applyTo,
13923
- ...a.description ? { description: a.description } : {}
13924
- }));
13925
- const agentrcConfig = {};
13926
- if (workspaces.length > 0) agentrcConfig.workspaces = workspaces;
13927
- if (standaloneAreas.length > 0) agentrcConfig.areas = standaloneAreas;
13928
- if (agentrcConfig.workspaces || agentrcConfig.areas) {
13929
- const configContent = JSON.stringify(agentrcConfig, null, 2) + "\n";
13930
- const { wrote } = await safeWriteFile(configPath, configContent, Boolean(options.force));
13931
- const rel = path16.relative(process.cwd(), configPath);
13932
- allFiles.push({ path: rel, action: wrote ? "wrote" : "skipped" });
13933
- if (shouldLog(options)) {
13934
- process.stderr.write((wrote ? `Wrote ${rel}` : `Skipped ${rel} (exists)`) + "\n");
13935
- }
14433
+ {
14434
+ const result = await scaffoldAgentrcConfig(
14435
+ repoPath,
14436
+ analysis.areas ?? [],
14437
+ Boolean(options.force)
14438
+ );
14439
+ const rel = path18.relative(process.cwd(), result.configPath);
14440
+ allFiles.push({ path: rel, action: result.wrote ? "wrote" : "skipped" });
14441
+ if (shouldLog(options)) {
14442
+ process.stderr.write((result.wrote ? `Wrote ${rel}` : `Skipped ${rel} (exists)`) + "\n");
13936
14443
  }
13937
14444
  }
13938
14445
  if (options.json) {
@@ -13949,7 +14456,7 @@ async function initCommand(repoPathArg, options) {
13949
14456
  `);
13950
14457
  }
13951
14458
  process.stderr.write("\nNext steps:\n");
13952
- process.stderr.write(" agentrc readiness Check AI readiness across 9 pillars\n");
14459
+ process.stderr.write(" agentrc readiness Run readiness report across 9 pillars\n");
13953
14460
  if (analysis.areas && analysis.areas.length > 0) {
13954
14461
  process.stderr.write(" agentrc instructions --areas Generate per-area instructions\n");
13955
14462
  }
@@ -13958,7 +14465,7 @@ async function initCommand(repoPathArg, options) {
13958
14465
  }
13959
14466
 
13960
14467
  // src/commands/pr.ts
13961
- import path17 from "path";
14468
+ import path19 from "path";
13962
14469
  var DEFAULT_PR_BRANCH = "agentrc/add-ai-config";
13963
14470
  async function prCommand(repo, options) {
13964
14471
  const provider = options.provider ?? "github";
@@ -13992,7 +14499,7 @@ async function prCommand(repo, options) {
13992
14499
  try {
13993
14500
  progress.update("Fetching repo info...");
13994
14501
  const repoInfo = await getRepo(token2, organization, project, name2);
13995
- const cacheRoot = path17.join(process.cwd(), ".agentrc-cache");
14502
+ const cacheRoot = path19.join(process.cwd(), ".agentrc-cache");
13996
14503
  const repoPath = validateCachePath(cacheRoot, organization, project, name2);
13997
14504
  await ensureDir(repoPath);
13998
14505
  if (!await isGitRepo(repoPath)) {
@@ -14006,8 +14513,8 @@ async function prCommand(repo, options) {
14006
14513
  await checkoutBranch(repoPath, branch);
14007
14514
  progress.update("Generating instructions...");
14008
14515
  const instructions = await generateCopilotInstructions({ repoPath, model: options.model });
14009
- const instructionsPath = path17.join(repoPath, ".github", "copilot-instructions.md");
14010
- await ensureDir(path17.dirname(instructionsPath));
14516
+ const instructionsPath = path19.join(repoPath, ".github", "copilot-instructions.md");
14517
+ await ensureDir(path19.dirname(instructionsPath));
14011
14518
  const { wrote, reason } = await safeWriteFile(instructionsPath, instructions, true);
14012
14519
  if (!wrote) {
14013
14520
  throw new Error(
@@ -14074,7 +14581,7 @@ async function prCommand(repo, options) {
14074
14581
  try {
14075
14582
  progress.update("Fetching repo info...");
14076
14583
  const repoInfo = await getRepo2(token, owner, name);
14077
- const cacheRoot = path17.join(process.cwd(), ".agentrc-cache");
14584
+ const cacheRoot = path19.join(process.cwd(), ".agentrc-cache");
14078
14585
  const repoPath = validateCachePath(cacheRoot, owner, name);
14079
14586
  await ensureDir(repoPath);
14080
14587
  if (!await isGitRepo(repoPath)) {
@@ -14086,8 +14593,8 @@ async function prCommand(repo, options) {
14086
14593
  await checkoutBranch(repoPath, branch);
14087
14594
  progress.update("Generating instructions...");
14088
14595
  const instructions = await generateCopilotInstructions({ repoPath, model: options.model });
14089
- const instructionsPath = path17.join(repoPath, ".github", "copilot-instructions.md");
14090
- await ensureDir(path17.dirname(instructionsPath));
14596
+ const instructionsPath = path19.join(repoPath, ".github", "copilot-instructions.md");
14597
+ await ensureDir(path19.dirname(instructionsPath));
14091
14598
  const { wrote, reason } = await safeWriteFile(instructionsPath, instructions, true);
14092
14599
  if (!wrote) {
14093
14600
  throw new Error(
@@ -14136,13 +14643,13 @@ async function prCommand(repo, options) {
14136
14643
  }
14137
14644
 
14138
14645
  // src/commands/readiness.ts
14139
- import path18 from "path";
14646
+ import path20 from "path";
14140
14647
  import chalk3 from "chalk";
14141
14648
  async function readinessCommand(repoPathArg, options) {
14142
- const repoPath = path18.resolve(repoPathArg ?? process.cwd());
14143
- const repoName = path18.basename(repoPath);
14144
- const resolvedOutputPath = options.output ? path18.resolve(options.output) : "";
14145
- const outputExt = options.output ? path18.extname(options.output).toLowerCase() : "";
14649
+ const repoPath = path20.resolve(repoPathArg ?? process.cwd());
14650
+ const repoName = path20.basename(repoPath);
14651
+ const resolvedOutputPath = options.output ? path20.resolve(options.output) : "";
14652
+ const outputExt = options.output ? path20.extname(options.output).toLowerCase() : "";
14146
14653
  let failLevelError;
14147
14654
  let report;
14148
14655
  try {
@@ -14201,10 +14708,10 @@ async function readinessCommand(repoPathArg, options) {
14201
14708
  if (options.visual || outputExt === ".html") {
14202
14709
  const html = generateVisualReport({
14203
14710
  reports: [{ repo: repoName, report }],
14204
- title: `AI Readiness Report: ${repoName}`,
14711
+ title: `Readiness Report: ${repoName}`,
14205
14712
  generatedAt: (/* @__PURE__ */ new Date()).toISOString()
14206
14713
  });
14207
- const outputPath = options.output ? resolvedOutputPath : path18.join(repoPath, "readiness-report.html");
14714
+ const outputPath = options.output ? resolvedOutputPath : path20.join(repoPath, "readiness-report.html");
14208
14715
  const { wrote, reason } = await safeWriteFile(outputPath, html, Boolean(options.force));
14209
14716
  if (!wrote) {
14210
14717
  const why = reason === "symlink" ? "path is a symlink" : "file exists (use --force)";
@@ -14301,7 +14808,7 @@ ${label}`));
14301
14808
  }
14302
14809
  }
14303
14810
  if (report.extras.length) {
14304
- log(chalk3.bold("\nAI readiness extras"));
14811
+ log(chalk3.bold("\nReadiness extras"));
14305
14812
  for (const extra of report.extras) {
14306
14813
  const icon = extra.status === "pass" ? chalk3.green("\u2714") : chalk3.red("\u2716");
14307
14814
  log(`${icon} ${extra.title}`);
@@ -14366,7 +14873,7 @@ function printAreaBreakdown(areaReports) {
14366
14873
  }
14367
14874
  function formatReadinessMarkdown(report, repoName) {
14368
14875
  const lines = [];
14369
- lines.push(`# AI Readiness Report: ${repoName}`);
14876
+ lines.push(`# Readiness Report: ${repoName}`);
14370
14877
  lines.push("");
14371
14878
  lines.push(`**Level ${report.achievedLevel}** \u2014 ${levelName(report.achievedLevel)}`);
14372
14879
  lines.push("");
@@ -14399,7 +14906,7 @@ function formatReadinessMarkdown(report, repoName) {
14399
14906
  lines.push("");
14400
14907
  }
14401
14908
  if (report.extras.length > 0) {
14402
- lines.push("## AI Readiness Extras");
14909
+ lines.push("## Readiness Extras");
14403
14910
  lines.push("");
14404
14911
  for (const extra of report.extras) {
14405
14912
  const icon = extra.status === "pass" ? "\u2705" : "\u274C";
@@ -14439,26 +14946,32 @@ function formatReadinessMarkdown(report, repoName) {
14439
14946
  }
14440
14947
 
14441
14948
  // src/commands/tui.tsx
14442
- import path20 from "path";
14949
+ import path22 from "path";
14443
14950
  import { render as render3 } from "ink";
14444
14951
 
14445
14952
  // src/ui/tui.tsx
14446
14953
  import fs10 from "fs/promises";
14447
- import path19 from "path";
14954
+ import path21 from "path";
14448
14955
  import { Box as Box5, Text as Text5, useApp as useApp4, useInput as useInput4, useStdout, useIsScreenReaderEnabled as useIsScreenReaderEnabled5 } from "ink";
14449
14956
  import { useEffect as useEffect5, useMemo, useState as useState5 } from "react";
14450
14957
  import { Fragment, jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
14451
14958
  var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
14452
14959
  function useTerminalColumns() {
14453
14960
  const { stdout } = useStdout();
14961
+ const accessible = useIsScreenReaderEnabled5();
14454
14962
  const [columns, setColumns] = useState5(stdout.columns ?? 80);
14455
14963
  useEffect5(() => {
14456
- const onResize = () => setColumns(stdout.columns ?? 80);
14457
- stdout.on("resize", onResize);
14964
+ const onResize = () => {
14965
+ if (!accessible) {
14966
+ stdout.write("\x1B[2J\x1B[H");
14967
+ }
14968
+ setColumns(stdout.columns ?? 80);
14969
+ };
14970
+ stdout.prependListener("resize", onResize);
14458
14971
  return () => {
14459
14972
  stdout.off("resize", onResize);
14460
14973
  };
14461
- }, [stdout]);
14974
+ }, [stdout, accessible]);
14462
14975
  return columns;
14463
14976
  }
14464
14977
  function useSpinner(active) {
@@ -14543,6 +15056,7 @@ function AgentRCTui({ repoPath, skipAnimation = false }) {
14543
15056
  const [modelPickTarget, setModelPickTarget] = useState5("eval");
14544
15057
  const [modelCursor, setModelCursor] = useState5(0);
14545
15058
  const [hasEvalConfig, setHasEvalConfig] = useState5(null);
15059
+ const [hasAgentrcConfig, setHasAgentrcConfig] = useState5(null);
14546
15060
  const [activityLog, setActivityLog] = useState5([]);
14547
15061
  const [generateTarget, setGenerateTarget] = useState5(
14548
15062
  "copilot-instructions"
@@ -14552,9 +15066,9 @@ function AgentRCTui({ repoPath, skipAnimation = false }) {
14552
15066
  const [isMonorepo, setIsMonorepo] = useState5(false);
14553
15067
  const [repoAreas, setRepoAreas] = useState5([]);
14554
15068
  const [areaCursor, setAreaCursor] = useState5(0);
14555
- const repoLabel = useMemo(() => path19.basename(repoPath), [repoPath]);
15069
+ const repoLabel = useMemo(() => path21.basename(repoPath), [repoPath]);
14556
15070
  const repoFull = useMemo(() => repoPath, [repoPath]);
14557
- const isLoading = status === "generating" || status === "bootstrapping" || status === "evaluating" || status === "generating-areas";
15071
+ const isLoading = status === "generating" || status === "bootstrapping" || status === "evaluating" || status === "generating-areas" || status === "readiness-running";
14558
15072
  const isMenu = status === "model-pick" || status === "eval-pick" || status === "batch-pick" || status === "generate-pick" || status === "generate-app-pick" || status === "generate-area-pick";
14559
15073
  const spinner = useSpinner(isLoading);
14560
15074
  const addLog = (text, type = "info") => {
@@ -14564,14 +15078,24 @@ function AgentRCTui({ repoPath, skipAnimation = false }) {
14564
15078
  setStatus("idle");
14565
15079
  };
14566
15080
  useEffect5(() => {
14567
- const configPath = path19.join(repoPath, "agentrc.eval.json");
15081
+ const configPath = path21.join(repoPath, "agentrc.eval.json");
14568
15082
  fs10.access(configPath).then(() => setHasEvalConfig(true)).catch(() => setHasEvalConfig(false));
15083
+ const agentrcConfigCandidates = [
15084
+ path21.join(repoPath, "agentrc.config.json"),
15085
+ path21.join(repoPath, ".github", "agentrc.config.json")
15086
+ ];
15087
+ Promise.all(
15088
+ agentrcConfigCandidates.map(
15089
+ (p) => fs10.access(p).then(() => true).catch(() => false)
15090
+ )
15091
+ ).then((results) => setHasAgentrcConfig(results.some(Boolean))).catch(() => setHasAgentrcConfig(false));
14569
15092
  analyzeRepo(repoPath).then((analysis) => {
14570
15093
  const apps = analysis.apps ?? [];
14571
15094
  setRepoApps(apps);
14572
15095
  setIsMonorepo(analysis.isMonorepo ?? false);
14573
15096
  setRepoAreas(analysis.areas ?? []);
14574
15097
  }).catch((err) => {
15098
+ setRepoAreas([]);
14575
15099
  addLog(`Repo analysis failed: ${err instanceof Error ? err.message : "unknown"}`, "error");
14576
15100
  });
14577
15101
  }, [repoPath]);
@@ -14579,17 +15103,17 @@ function AgentRCTui({ repoPath, skipAnimation = false }) {
14579
15103
  let active = true;
14580
15104
  listCopilotModels().then((models) => {
14581
15105
  if (!active) return;
14582
- setAvailableModels(models);
14583
- if (models.length === 0) return;
15106
+ const list = models.length > 0 ? models : PREFERRED_MODELS;
15107
+ setAvailableModels(list);
14584
15108
  setEvalModel(
14585
- (current) => models.includes(current) ? current : pickBestModel(models, current)
15109
+ (current) => list.includes(current) ? current : pickBestModel(list, current)
14586
15110
  );
14587
15111
  setJudgeModel(
14588
- (current) => models.includes(current) ? current : pickBestModel(models, current)
15112
+ (current) => list.includes(current) ? current : pickBestModel(list, current)
14589
15113
  );
14590
15114
  }).catch(() => {
14591
15115
  if (!active) return;
14592
- setAvailableModels([]);
15116
+ setAvailableModels(PREFERRED_MODELS);
14593
15117
  });
14594
15118
  return () => {
14595
15119
  active = false;
@@ -14625,7 +15149,7 @@ function AgentRCTui({ repoPath, skipAnimation = false }) {
14625
15149
  };
14626
15150
  useEffect5(() => {
14627
15151
  let active = true;
14628
- const configPath = path19.join(repoPath, "agentrc.eval.json");
15152
+ const configPath = path21.join(repoPath, "agentrc.eval.json");
14629
15153
  fs10.readFile(configPath, "utf8").then((raw) => {
14630
15154
  if (!active) return;
14631
15155
  const parsed = JSON.parse(raw);
@@ -14640,7 +15164,7 @@ function AgentRCTui({ repoPath, skipAnimation = false }) {
14640
15164
  };
14641
15165
  }, [repoPath]);
14642
15166
  const bootstrapEvalConfig = async (count, force) => {
14643
- const configPath = path19.join(repoPath, "agentrc.eval.json");
15167
+ const configPath = path21.join(repoPath, "agentrc.eval.json");
14644
15168
  try {
14645
15169
  setStatus("bootstrapping");
14646
15170
  setMessage("Generating eval cases with Copilot SDK...");
@@ -14679,13 +15203,13 @@ function AgentRCTui({ repoPath, skipAnimation = false }) {
14679
15203
  if (status === "preview") {
14680
15204
  if (input.toLowerCase() === "s") {
14681
15205
  try {
14682
- const outputPath = generateSavePath || path19.join(repoPath, ".github", "copilot-instructions.md");
14683
- await fs10.mkdir(path19.dirname(outputPath), { recursive: true });
15206
+ const outputPath = generateSavePath || path21.join(repoPath, ".github", "copilot-instructions.md");
15207
+ await fs10.mkdir(path21.dirname(outputPath), { recursive: true });
14684
15208
  const { wrote, reason } = await safeWriteFile(outputPath, generatedContent, true);
14685
15209
  if (!wrote)
14686
15210
  throw new Error(reason === "symlink" ? "Path is a symlink" : "Write failed");
14687
15211
  setStatus("done");
14688
- const relPath = path19.relative(repoPath, outputPath);
15212
+ const relPath = path21.relative(repoPath, outputPath);
14689
15213
  const msg = `Saved to ${relPath}`;
14690
15214
  setMessage(msg);
14691
15215
  addLog(msg, "success");
@@ -14719,7 +15243,7 @@ function AgentRCTui({ repoPath, skipAnimation = false }) {
14719
15243
  setMessage("Enter a positive number of eval cases, then press Enter.");
14720
15244
  return;
14721
15245
  }
14722
- const configPath = path19.join(repoPath, "agentrc.eval.json");
15246
+ const configPath = path21.join(repoPath, "agentrc.eval.json");
14723
15247
  setEvalBootstrapCount(count);
14724
15248
  try {
14725
15249
  await fs10.access(configPath);
@@ -14778,7 +15302,7 @@ function AgentRCTui({ repoPath, skipAnimation = false }) {
14778
15302
  setStatus("generate-app-pick");
14779
15303
  setMessage("Generate for root or per-app?");
14780
15304
  } else {
14781
- const savePath = path19.join(repoPath, ".github", "copilot-instructions.md");
15305
+ const savePath = path21.join(repoPath, ".github", "copilot-instructions.md");
14782
15306
  setGenerateSavePath(savePath);
14783
15307
  await doGenerate(repoPath, savePath, "copilot-instructions");
14784
15308
  }
@@ -14790,20 +15314,20 @@ function AgentRCTui({ repoPath, skipAnimation = false }) {
14790
15314
  setStatus("generate-app-pick");
14791
15315
  setMessage("Generate for root or per-app?");
14792
15316
  } else {
14793
- const savePath = path19.join(repoPath, "AGENTS.md");
15317
+ const savePath = path21.join(repoPath, "AGENTS.md");
14794
15318
  setGenerateSavePath(savePath);
14795
15319
  await doGenerate(repoPath, savePath, "agents-md");
14796
15320
  }
14797
15321
  return;
14798
15322
  }
14799
- if (input.toLowerCase() === "f") {
15323
+ if (input.toLowerCase() === "n") {
14800
15324
  if (repoAreas.length === 0) {
14801
15325
  setMessage("No areas detected. Add agentrc.config.json to define areas.");
14802
15326
  return;
14803
15327
  }
14804
15328
  setAreaCursor(0);
14805
15329
  setStatus("generate-area-pick");
14806
- setMessage("Generate file-based instructions for areas.");
15330
+ setMessage("Generate nested instructions for areas.");
14807
15331
  return;
14808
15332
  }
14809
15333
  if (key.escape) {
@@ -14815,7 +15339,7 @@ function AgentRCTui({ repoPath, skipAnimation = false }) {
14815
15339
  }
14816
15340
  if (status === "generate-app-pick") {
14817
15341
  if (input.toLowerCase() === "r") {
14818
- const savePath = generateTarget === "copilot-instructions" ? path19.join(repoPath, ".github", "copilot-instructions.md") : path19.join(repoPath, "AGENTS.md");
15342
+ const savePath = generateTarget === "copilot-instructions" ? path21.join(repoPath, ".github", "copilot-instructions.md") : path21.join(repoPath, "AGENTS.md");
14819
15343
  setGenerateSavePath(savePath);
14820
15344
  await doGenerate(repoPath, savePath, generateTarget);
14821
15345
  return;
@@ -14825,7 +15349,7 @@ function AgentRCTui({ repoPath, skipAnimation = false }) {
14825
15349
  addLog(`Generating ${generateTarget} for ${repoApps.length} apps...`, "progress");
14826
15350
  let count = 0;
14827
15351
  for (const app2 of repoApps) {
14828
- const savePath = generateTarget === "copilot-instructions" ? path19.join(app2.path, ".github", "copilot-instructions.md") : path19.join(app2.path, "AGENTS.md");
15352
+ const savePath = generateTarget === "copilot-instructions" ? path21.join(app2.path, ".github", "copilot-instructions.md") : path21.join(app2.path, "AGENTS.md");
14829
15353
  setMessage(`Generating for ${app2.name} (${count + 1}/${repoApps.length})...`);
14830
15354
  try {
14831
15355
  const content = await generateCopilotInstructions({
@@ -14833,11 +15357,11 @@ function AgentRCTui({ repoPath, skipAnimation = false }) {
14833
15357
  onProgress: (msg) => setMessage(`${app2.name}: ${msg}`)
14834
15358
  });
14835
15359
  if (content.trim()) {
14836
- await fs10.mkdir(path19.dirname(savePath), { recursive: true });
15360
+ await fs10.mkdir(path21.dirname(savePath), { recursive: true });
14837
15361
  const { wrote: saved } = await safeWriteFile(savePath, content, true);
14838
15362
  if (!saved) continue;
14839
15363
  count++;
14840
- addLog(`${app2.name}: saved ${path19.basename(savePath)}`, "success");
15364
+ addLog(`${app2.name}: saved ${path21.basename(savePath)}`, "success");
14841
15365
  }
14842
15366
  } catch (error) {
14843
15367
  const msg = error instanceof Error ? error.message : "Failed.";
@@ -14851,7 +15375,7 @@ function AgentRCTui({ repoPath, skipAnimation = false }) {
14851
15375
  const num = Number.parseInt(input, 10);
14852
15376
  if (Number.isFinite(num) && num >= 1 && num <= repoApps.length) {
14853
15377
  const app2 = repoApps[num - 1];
14854
- const savePath = generateTarget === "copilot-instructions" ? path19.join(app2.path, ".github", "copilot-instructions.md") : path19.join(app2.path, "AGENTS.md");
15378
+ const savePath = generateTarget === "copilot-instructions" ? path21.join(app2.path, ".github", "copilot-instructions.md") : path21.join(app2.path, "AGENTS.md");
14855
15379
  setGenerateSavePath(savePath);
14856
15380
  await doGenerate(app2.path, savePath, generateTarget);
14857
15381
  return;
@@ -14866,10 +15390,7 @@ function AgentRCTui({ repoPath, skipAnimation = false }) {
14866
15390
  if (status === "generate-area-pick") {
14867
15391
  if (input.toLowerCase() === "a") {
14868
15392
  setStatus("generating-areas");
14869
- addLog(
14870
- `Generating file-based instructions for ${repoAreas.length} areas...`,
14871
- "progress"
14872
- );
15393
+ addLog(`Generating nested instructions for ${repoAreas.length} areas...`, "progress");
14873
15394
  let written = 0;
14874
15395
  for (const [i, area] of repoAreas.entries()) {
14875
15396
  setMessage(`Generating for "${area.name}" (${i + 1}/${repoAreas.length})...`);
@@ -14882,7 +15403,7 @@ function AgentRCTui({ repoPath, skipAnimation = false }) {
14882
15403
  const result = await writeAreaInstruction(repoPath, area, body);
14883
15404
  if (result.status === "written") {
14884
15405
  written++;
14885
- addLog(`${area.name}: saved ${path19.basename(result.filePath)}`, "success");
15406
+ addLog(`${area.name}: saved ${path21.basename(result.filePath)}`, "success");
14886
15407
  } else if (result.status === "skipped") {
14887
15408
  addLog(`${area.name}: skipped (file exists)`, "info");
14888
15409
  }
@@ -14892,9 +15413,7 @@ function AgentRCTui({ repoPath, skipAnimation = false }) {
14892
15413
  }
14893
15414
  }
14894
15415
  setStatus("done");
14895
- setMessage(
14896
- `Generated file-based instructions for ${written}/${repoAreas.length} areas.`
14897
- );
15416
+ setMessage(`Generated nested instructions for ${written}/${repoAreas.length} areas.`);
14898
15417
  return;
14899
15418
  }
14900
15419
  if (key.upArrow) {
@@ -14910,7 +15429,7 @@ function AgentRCTui({ repoPath, skipAnimation = false }) {
14910
15429
  if (!area) return;
14911
15430
  setStatus("generating-areas");
14912
15431
  setMessage(`Generating for "${area.name}"...`);
14913
- addLog(`Generating file-based instructions for "${area.name}"...`, "progress");
15432
+ addLog(`Generating nested instructions for "${area.name}"...`, "progress");
14914
15433
  try {
14915
15434
  const body = await generateAreaInstructions({
14916
15435
  repoPath,
@@ -14978,8 +15497,8 @@ function AgentRCTui({ repoPath, skipAnimation = false }) {
14978
15497
  }
14979
15498
  if (status === "eval-pick") {
14980
15499
  if (input.toLowerCase() === "r") {
14981
- const configPath = path19.join(repoPath, "agentrc.eval.json");
14982
- const outputPath = path19.join(
15500
+ const configPath = path21.join(repoPath, "agentrc.eval.json");
15501
+ const outputPath = path21.join(
14983
15502
  repoPath,
14984
15503
  ".agentrc",
14985
15504
  "evals",
@@ -15083,6 +15602,36 @@ function AgentRCTui({ repoPath, skipAnimation = false }) {
15083
15602
  setMessage("Select what to generate.");
15084
15603
  return;
15085
15604
  }
15605
+ if (input.toLowerCase() === "r") {
15606
+ setStatus("readiness-running");
15607
+ setMessage("Running readiness report\u2026");
15608
+ addLog("Running readiness report\u2026", "progress");
15609
+ try {
15610
+ let policies;
15611
+ try {
15612
+ const config = await loadAgentrcConfig(repoPath);
15613
+ policies = config?.policies;
15614
+ } catch {
15615
+ }
15616
+ const report = await runReadinessReport({ repoPath, policies });
15617
+ const levelName2 = getLevelName(report.achievedLevel);
15618
+ const failCount = report.criteria.filter((c) => c.status === "fail").length;
15619
+ addLog(
15620
+ `Level ${report.achievedLevel} (${levelName2}) \u2014 ${failCount} item(s) to fix`,
15621
+ "success"
15622
+ );
15623
+ setStatus("done");
15624
+ setMessage(
15625
+ `Readiness: Level ${report.achievedLevel} (${levelName2}). ${failCount} improvement(s) available.`
15626
+ );
15627
+ } catch (error) {
15628
+ const msg = error instanceof Error ? error.message : "Failed.";
15629
+ addLog(`Readiness failed: ${msg}`, "error");
15630
+ setStatus("error");
15631
+ setMessage(`Readiness failed: ${msg}`);
15632
+ }
15633
+ return;
15634
+ }
15086
15635
  if (input.toLowerCase() === "b") {
15087
15636
  setStatus("batch-pick");
15088
15637
  setMessage("Select batch provider.");
@@ -15093,6 +15642,36 @@ function AgentRCTui({ repoPath, skipAnimation = false }) {
15093
15642
  setMessage("Select eval action.");
15094
15643
  return;
15095
15644
  }
15645
+ if (input.toLowerCase() === "c") {
15646
+ if (hasAgentrcConfig) {
15647
+ setMessage("agentrc.config.json already exists.");
15648
+ return;
15649
+ }
15650
+ setStatus("generating");
15651
+ setMessage("Creating agentrc.config.json...");
15652
+ addLog("Scaffolding agentrc.config.json...", "progress");
15653
+ try {
15654
+ const result = await scaffoldAgentrcConfig(repoPath, repoAreas);
15655
+ if (result.wrote) {
15656
+ setHasAgentrcConfig(true);
15657
+ setStatus("done");
15658
+ const msg = `Created agentrc.config.json`;
15659
+ setMessage(msg);
15660
+ addLog(msg, "success");
15661
+ } else {
15662
+ setHasAgentrcConfig(true);
15663
+ setStatus("idle");
15664
+ setMessage("agentrc.config.json already exists (skipped).");
15665
+ addLog("agentrc.config.json already exists.", "info");
15666
+ }
15667
+ } catch (error) {
15668
+ setStatus("error");
15669
+ const msg = error instanceof Error ? error.message : "Failed to create config.";
15670
+ setMessage(msg);
15671
+ addLog(msg, "error");
15672
+ }
15673
+ return;
15674
+ }
15096
15675
  if (input.toLowerCase() === "m") {
15097
15676
  if (hideModelPicker) {
15098
15677
  setMessage(
@@ -15135,7 +15714,7 @@ function AgentRCTui({ repoPath, skipAnimation = false }) {
15135
15714
  { isActive: inputActive }
15136
15715
  );
15137
15716
  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;
15717
+ 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
15718
  const statusColor = status === "error" ? "red" : status === "done" ? "green" : isLoading ? "yellow" : isMenu ? "magentaBright" : "cyanBright";
15140
15719
  const formatTokens = (result) => {
15141
15720
  const withUsage = result.metrics?.withInstructions?.tokenUsage;
@@ -15207,6 +15786,10 @@ function AgentRCTui({ repoPath, skipAnimation = false }) {
15207
15786
  /* @__PURE__ */ jsxs4(Text5, { children: [
15208
15787
  /* @__PURE__ */ jsx7(Text5, { color: "gray", children: "Eval " }),
15209
15788
  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" })
15789
+ ] }),
15790
+ /* @__PURE__ */ jsxs4(Text5, { children: [
15791
+ /* @__PURE__ */ jsx7(Text5, { color: "gray", children: "Config " }),
15792
+ 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" })
15210
15793
  ] })
15211
15794
  ] }),
15212
15795
  /* @__PURE__ */ jsx7(Divider, { columns: terminalColumns, label: "Activity", accessible }),
@@ -15282,19 +15865,12 @@ function AgentRCTui({ repoPath, skipAnimation = false }) {
15282
15865
  /* @__PURE__ */ jsx7(Text5, { color: "white", children: app2.name }),
15283
15866
  /* @__PURE__ */ jsxs4(Text5, { color: "gray", dimColor: true, children: [
15284
15867
  " ",
15285
- path19.relative(repoPath, app2.path)
15868
+ path21.relative(repoPath, app2.path)
15286
15869
  ] })
15287
15870
  ] }, app2.name)) })
15288
15871
  ] }),
15289
15872
  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
- ),
15873
+ /* @__PURE__ */ jsx7(Divider, { columns: terminalColumns, label: "Nested instructions", accessible }),
15298
15874
  /* @__PURE__ */ jsx7(Box5, { flexDirection: "column", paddingLeft: 1, children: repoAreas.map((area, i) => /* @__PURE__ */ jsxs4(Text5, { children: [
15299
15875
  /* @__PURE__ */ jsx7(Text5, { color: i === areaCursor ? "cyanBright" : "gray", children: i === areaCursor ? accessible ? ">" : "\u25B6" : " " }),
15300
15876
  /* @__PURE__ */ jsx7(Text5, { color: "gray", children: " " }),
@@ -15323,7 +15899,7 @@ function AgentRCTui({ repoPath, skipAnimation = false }) {
15323
15899
  children: [
15324
15900
  /* @__PURE__ */ jsxs4(Text5, { color: "cyan", bold: true, children: [
15325
15901
  "Preview (",
15326
- path19.relative(repoPath, generateSavePath) || generateTarget,
15902
+ path21.relative(repoPath, generateSavePath) || generateTarget,
15327
15903
  ")"
15328
15904
  ] }),
15329
15905
  /* @__PURE__ */ jsx7(Text5, { color: "gray", children: truncatedPreview })
@@ -15375,9 +15951,9 @@ function AgentRCTui({ repoPath, skipAnimation = false }) {
15375
15951
  /* @__PURE__ */ jsx7(Text5, { color: "cyan", children: " to select " }),
15376
15952
  /* @__PURE__ */ jsx7(KeyHint, { k: "Esc", label: "Back" })
15377
15953
  ] }) : 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)" }),
15954
+ /* @__PURE__ */ jsx7(KeyHint, { k: "C", label: "Instructions" }),
15955
+ /* @__PURE__ */ jsx7(KeyHint, { k: "A", label: "Agents" }),
15956
+ repoAreas.length > 0 && /* @__PURE__ */ jsx7(KeyHint, { k: "N", label: "Nested (areas)" }),
15381
15957
  /* @__PURE__ */ jsx7(KeyHint, { k: "Esc", label: "Back" })
15382
15958
  ] }) : status === "generate-app-pick" ? /* @__PURE__ */ jsxs4(Box5, { children: [
15383
15959
  /* @__PURE__ */ jsx7(KeyHint, { k: "R", label: "Root only" }),
@@ -15413,12 +15989,14 @@ function AgentRCTui({ repoPath, skipAnimation = false }) {
15413
15989
  ] }) : isLoading ? /* @__PURE__ */ jsx7(Box5, { children: /* @__PURE__ */ jsx7(KeyHint, { k: "Q", label: "Quit" }) }) : /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", children: [
15414
15990
  /* @__PURE__ */ jsxs4(Box5, { children: [
15415
15991
  /* @__PURE__ */ jsx7(KeyHint, { k: "G", label: "Generate" }),
15992
+ /* @__PURE__ */ jsx7(KeyHint, { k: "R", label: "Readiness" }),
15416
15993
  /* @__PURE__ */ jsx7(KeyHint, { k: "E", label: "Eval" }),
15417
15994
  /* @__PURE__ */ jsx7(KeyHint, { k: "B", label: "Batch" })
15418
15995
  ] }),
15419
15996
  /* @__PURE__ */ jsxs4(Box5, { children: [
15420
15997
  /* @__PURE__ */ jsx7(KeyHint, { k: "M", label: "Model" }),
15421
15998
  /* @__PURE__ */ jsx7(KeyHint, { k: "J", label: "Judge" }),
15999
+ hasAgentrcConfig === false && /* @__PURE__ */ jsx7(KeyHint, { k: "C", label: "Create config" }),
15422
16000
  /* @__PURE__ */ jsx7(KeyHint, { k: "Q", label: "Quit" })
15423
16001
  ] })
15424
16002
  ] }) })
@@ -15430,7 +16008,7 @@ function AgentRCTui({ repoPath, skipAnimation = false }) {
15430
16008
  // src/commands/tui.tsx
15431
16009
  import { jsx as jsx8 } from "react/jsx-runtime";
15432
16010
  async function tuiCommand(options) {
15433
- const repoPath = path20.resolve(options.repo ?? process.cwd());
16011
+ const repoPath = path22.resolve(options.repo ?? process.cwd());
15434
16012
  const skipAnimation = options.animation === false;
15435
16013
  try {
15436
16014
  const accessible = options.accessible ? true : void 0;
@@ -15445,7 +16023,7 @@ async function tuiCommand(options) {
15445
16023
  }
15446
16024
 
15447
16025
  // src/cli.ts
15448
- var _require = createRequire(import.meta.url);
16026
+ var _require = createRequire2(import.meta.url);
15449
16027
  var CLI_VERSION = _require("../package.json").version;
15450
16028
  function withGlobalOpts(fn) {
15451
16029
  return async (...raw) => {
@@ -15466,7 +16044,7 @@ function withGlobalOpts(fn) {
15466
16044
  function runCli(argv) {
15467
16045
  const program = new Command();
15468
16046
  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));
16047
+ 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
16048
  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
16049
  program.command("generate").description("Generate instructions, agents, MCP, or VS Code configs").addArgument(
15472
16050
  new Argument("<type>", "Config type to generate").choices([
@@ -15475,14 +16053,14 @@ function runCli(argv) {
15475
16053
  "mcp",
15476
16054
  "vscode"
15477
16055
  ])
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));
16056
+ ).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)").option("--dry-run", "Preview generated files without writing anything").action(withGlobalOpts(generateCommand));
15479
16057
  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
16058
  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
16059
  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));
16060
+ 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));
16061
+ 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
16062
  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));
16063
+ 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
16064
  program.parse(argv);
15487
16065
  }
15488
16066