rulesync 16.11.0 → 16.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -866,6 +866,7 @@ const ErrorCodes = {
866
866
  INIT_FAILED: "INIT_FAILED",
867
867
  MCP_FAILED: "MCP_FAILED",
868
868
  DOCTOR_FAILED: "DOCTOR_FAILED",
869
+ RELEASE_NOTES_FAILED: "RELEASE_NOTES_FAILED",
869
870
  UNKNOWN_ERROR: "UNKNOWN_ERROR"
870
871
  };
871
872
  /**
@@ -2452,34 +2453,52 @@ const AMP_HOOK_EVENTS = [
2452
2453
  ];
2453
2454
  /**
2454
2455
  * Hook events supported by Cline's file-based hooks. Cline resolves one
2455
- * executable per lifecycle event from its hooks directory, and the event names
2456
- * it accepts are fixed by `VALID_HOOK_TYPES` in
2457
- * `apps/vscode/src/core/hooks/utils.ts`: `TaskStart`, `TaskResume`,
2458
- * `TaskCancel`, `TaskComplete`, `PreToolUse`, `PostToolUse`,
2459
- * `UserPromptSubmit`, `Notification` and `PreCompact`.
2456
+ * executable per lifecycle event from its hooks directory, and the accepted
2457
+ * event names come from two runtimes that read the same directory:
2458
+ *
2459
+ * - The VS Code extension fixes them in `VALID_HOOK_TYPES`
2460
+ * (`apps/vscode/src/core/hooks/utils.ts`): `TaskStart`, `TaskResume`,
2461
+ * `TaskCancel`, `TaskComplete`, `PreToolUse`, `PostToolUse`,
2462
+ * `UserPromptSubmit`, `Notification` and `PreCompact`.
2463
+ * - The SDK/CLI fixes them in `HookConfigFileName`
2464
+ * (`sdk/packages/core/src/hooks/hook-file-config.ts`), which drops
2465
+ * `Notification` but adds `TaskError` (→ `agent_error`) and
2466
+ * `SessionShutdown` (→ `session_shutdown`).
2467
+ *
2468
+ * This set is the union, because `.clinerules/hooks` is in both runtimes'
2469
+ * search paths and a script named for an event the running one does not know
2470
+ * is simply never spawned. That holds for unknown *names* only: for an event a
2471
+ * runtime does know, the SDK/CLI spawns both the extensionless script and its
2472
+ * `.ps1` twin, so each generated script opens with a guard that stands down on
2473
+ * the platform the other one owns — see `generateClineHookScript` and
2474
+ * `generateClineHookPowerShellScript`.
2460
2475
  *
2461
2476
  * `TaskResume` and `TaskCancel` have no canonical counterpart and stay
2462
2477
  * unmapped rather than being approximated by `sessionEnd` / `stop`, whose
2463
2478
  * semantics differ.
2464
2479
  *
2465
2480
  * @see https://github.com/cline/cline/blob/main/apps/vscode/src/core/hooks/utils.ts
2481
+ * @see https://github.com/cline/cline/blob/main/sdk/packages/core/src/hooks/hook-file-config.ts
2466
2482
  */
2467
2483
  const CLINE_HOOK_EVENTS = [
2468
2484
  "sessionStart",
2485
+ "sessionEnd",
2469
2486
  "preToolUse",
2470
2487
  "postToolUse",
2471
2488
  "beforeSubmitPrompt",
2472
2489
  "preCompact",
2473
2490
  "notification",
2474
- "taskCompleted"
2491
+ "taskCompleted",
2492
+ "afterError"
2475
2493
  ];
2476
2494
  /**
2477
2495
  * Hook events supported by GitHub Copilot (cloud coding agent).
2478
2496
  *
2479
2497
  * The events rulesync writes to `.github/hooks/*.json`:
2480
2498
  * `sessionStart`, `sessionEnd`, `userPromptSubmitted` ← `beforeSubmitPrompt`,
2481
- * `preToolUse`, `postToolUse`, `agentStop` ← `stop`, `subagentStart`,
2482
- * `subagentStop`, `errorOccurred` ← `afterError`, and `preCompact`.
2499
+ * `preToolUse`, `postToolUse`, `postToolUseFailure`, `agentStop` ← `stop`,
2500
+ * `subagentStart`, `subagentStop`, `errorOccurred` ← `afterError`,
2501
+ * `preCompact`, and `userPromptTransformed` ← `userPromptExpansion`.
2483
2502
  *
2484
2503
  * `preCompact` and `subagentStart` are authorable because the unified hooks
2485
2504
  * reference's per-event "Cloud agent" column says both fire there. That column
@@ -2488,9 +2507,11 @@ const CLINE_HOOK_EVENTS = [
2488
2507
  * undo that. `notification` and `permissionRequest` stay out because the same
2489
2508
  * column is explicit that they do not fire on the cloud agent.
2490
2509
  *
2491
- * Two further CLI events — `postToolUseFailure` and `userPromptTransformed`
2492
- * ({@link COPILOTCLI_HOOK_EVENTS}) are documented as firing on the cloud
2493
- * agent as well but are not modelled here yet.
2510
+ * `postToolUseFailure` and `userPromptTransformed` are shared with
2511
+ * {@link COPILOTCLI_HOOK_EVENTS}; the same column records both as firing on the
2512
+ * cloud agent, so they are authorable here too. The event surfaces overlap but
2513
+ * the config surfaces do not: `copilot` emits `command` hooks only, while the
2514
+ * CLI adapter also handles `http` and `prompt`.
2494
2515
  *
2495
2516
  * @see https://docs.github.com/en/copilot/reference/hooks-reference
2496
2517
  */
@@ -2500,11 +2521,13 @@ const COPILOT_HOOK_EVENTS = [
2500
2521
  "beforeSubmitPrompt",
2501
2522
  "preToolUse",
2502
2523
  "postToolUse",
2524
+ "postToolUseFailure",
2503
2525
  "stop",
2504
2526
  "subagentStart",
2505
2527
  "subagentStop",
2506
2528
  "afterError",
2507
- "preCompact"
2529
+ "preCompact",
2530
+ "userPromptExpansion"
2508
2531
  ];
2509
2532
  /**
2510
2533
  * Hook events supported by the GitHub Copilot CLI (`copilotcli-hooks.ts`).
@@ -3220,15 +3243,21 @@ const CANONICAL_TO_AMP_EVENT_NAMES = {
3220
3243
  beforeSubmitPrompt: "agent.start",
3221
3244
  stop: "agent.end"
3222
3245
  };
3223
- /** Map canonical hook events to Cline's `VALID_HOOK_TYPES` file names. */
3246
+ /**
3247
+ * Map canonical hook events to Cline's hook script file names — the union of
3248
+ * the VS Code extension's `VALID_HOOK_TYPES` and the SDK/CLI's
3249
+ * `HookConfigFileName`, see {@link CLINE_HOOK_EVENTS}.
3250
+ */
3224
3251
  const CANONICAL_TO_CLINE_EVENT_NAMES = {
3225
3252
  sessionStart: "TaskStart",
3253
+ sessionEnd: "SessionShutdown",
3226
3254
  preToolUse: "PreToolUse",
3227
3255
  postToolUse: "PostToolUse",
3228
3256
  beforeSubmitPrompt: "UserPromptSubmit",
3229
3257
  preCompact: "PreCompact",
3230
3258
  notification: "Notification",
3231
- taskCompleted: "TaskComplete"
3259
+ taskCompleted: "TaskComplete",
3260
+ afterError: "TaskError"
3232
3261
  };
3233
3262
  /**
3234
3263
  * Map canonical camelCase event names to Copilot camelCase.
@@ -3239,11 +3268,13 @@ const CANONICAL_TO_COPILOT_EVENT_NAMES = {
3239
3268
  beforeSubmitPrompt: "userPromptSubmitted",
3240
3269
  preToolUse: "preToolUse",
3241
3270
  postToolUse: "postToolUse",
3271
+ postToolUseFailure: "postToolUseFailure",
3242
3272
  stop: "agentStop",
3243
3273
  subagentStart: "subagentStart",
3244
3274
  subagentStop: "subagentStop",
3245
3275
  afterError: "errorOccurred",
3246
- preCompact: "preCompact"
3276
+ preCompact: "preCompact",
3277
+ userPromptExpansion: "userPromptTransformed"
3247
3278
  };
3248
3279
  /**
3249
3280
  * Map Copilot camelCase event names to canonical camelCase.
@@ -5468,6 +5499,7 @@ const RulesyncSkillFrontmatterSchema = z.looseObject({
5468
5499
  })),
5469
5500
  cline: z.optional(z.looseObject({})),
5470
5501
  roo: z.optional(z.looseObject({})),
5502
+ amp: z.optional(z.looseObject({})),
5471
5503
  devin: z.optional(z.looseObject({
5472
5504
  "argument-hint": z.optional(z.string()),
5473
5505
  model: z.optional(z.string()),
@@ -11700,7 +11732,8 @@ const KiloCommandFrontmatterSchema = z.looseObject({
11700
11732
  description: z.optional(z.string()),
11701
11733
  agent: z.optional(z.string()),
11702
11734
  subtask: z.optional(z.boolean()),
11703
- model: z.optional(z.string())
11735
+ model: z.optional(z.string()),
11736
+ variant: z.optional(z.string())
11704
11737
  });
11705
11738
  var KiloCommand = class KiloCommand extends ToolCommand {
11706
11739
  frontmatter;
@@ -15382,6 +15415,13 @@ function generateClineHookScript({ event, commands }) {
15382
15415
  `# ${event} hook generated by rulesync — edit .rulesync/hooks.jsonc and regenerate.`,
15383
15416
  `# ${CLINE_HOOK_SCRIPT_MARKER}`,
15384
15417
  "",
15418
+ "case \"${OSTYPE:-$(uname -s 2>/dev/null || true)}\" in",
15419
+ " msys*|MSYS*|cygwin*|CYGWIN*|MINGW*|mingw*)",
15420
+ ` printf '{"cancel": false, "contextModification": "", "errorMessage": ""}\\n'`,
15421
+ " exit 0",
15422
+ " ;;",
15423
+ "esac",
15424
+ "",
15385
15425
  "payload=$(cat)",
15386
15426
  "cancel=false",
15387
15427
  "error_message=''",
@@ -15395,15 +15435,39 @@ function generateClineHookScript({ event, commands }) {
15395
15435
  return lines.join("\n");
15396
15436
  }
15397
15437
  /**
15398
- * The PowerShell twin of {@link generateClineHookScript}. On Windows Cline
15399
- * resolves only `<Event>.ps1` and runs it through `powershell -File`, so both
15400
- * spellings are written and the platform picks one.
15438
+ * The PowerShell twin of {@link generateClineHookScript}. On Windows the VS Code
15439
+ * extension resolves only `<Event>.ps1` and runs it through `powershell -File`,
15440
+ * so both spellings are written and the platform picks one.
15441
+ *
15442
+ * The SDK/CLI runtime does not pick one. `listHookConfigFiles` dedupes by path,
15443
+ * so `TaskError` and `TaskError.ps1` are two entries naming the same event, and
15444
+ * `createHookCommandMap` appends both to that event's command list without a
15445
+ * per-event dedupe — it then runs every command in the list, spawning a `.ps1`
15446
+ * through `pwsh` on Unix too. That produced noise rather than a second
15447
+ * execution (the body shells out through `cmd /c`, which Unix has no such
15448
+ * thing), but it is still a failure reported on every fire.
15449
+ *
15450
+ * Hence the leading platform guard: off Windows the script answers with the
15451
+ * neutral success payload and exits, leaving the POSIX twin to do the work.
15452
+ * Its counterpart at the top of {@link generateClineHookScript} covers the
15453
+ * quadrant where the duplication is real — Windows with a POSIX shell.
15454
+ * `$IsWindows` only exists in PowerShell 6+, and is `$null` under the Windows
15455
+ * PowerShell 5.1 that `powershell -File` starts — so the guard tests that it is
15456
+ * defined *and* false, rather than `-not $IsWindows`, which would be true on
15457
+ * 5.1 and would no-op the script on the one platform it exists for.
15458
+ *
15459
+ * @see https://github.com/cline/cline/blob/main/sdk/packages/core/src/hooks/hook-file-hooks.ts
15401
15460
  */
15402
15461
  function generateClineHookPowerShellScript({ event, commands }) {
15403
15462
  const lines = [
15404
15463
  `# ${event} hook generated by rulesync — edit .rulesync/hooks.jsonc and regenerate.`,
15405
15464
  `# ${CLINE_HOOK_SCRIPT_MARKER}`,
15406
15465
  "",
15466
+ "if ($null -ne $IsWindows -and -not $IsWindows) {",
15467
+ ` Write-Output '{"cancel": false, "contextModification": "", "errorMessage": ""}'`,
15468
+ " exit 0",
15469
+ "}",
15470
+ "",
15407
15471
  "$payload = [Console]::In.ReadToEnd()",
15408
15472
  "$cancel = $false",
15409
15473
  "$errorMessage = ''",
@@ -15465,10 +15529,14 @@ var ClineHookScript = class extends ToolFile {
15465
15529
  * by the contract, so every generated script carries a marker line and a script
15466
15530
  * without it is never overwritten.
15467
15531
  *
15468
- * Cline's CLI and SDK use a different, in-process hook surface (`AgentHooks`
15469
- * from `@cline/core`), which this adapter does not target.
15532
+ * The project hooks directory is in the search paths of both the VS Code
15533
+ * extension and the SDK/CLI, whose accepted event names differ slightly, so the
15534
+ * emitted set is their union ({@link CANONICAL_TO_CLINE_EVENT_NAMES}). Cline's
15535
+ * in-process hook surface (`AgentHooks` from `@cline/core`) is a separate
15536
+ * mechanism this adapter does not target.
15470
15537
  *
15471
15538
  * @see https://github.com/cline/cline/blob/main/apps/vscode/src/core/hooks/utils.ts
15539
+ * @see https://github.com/cline/cline/blob/main/sdk/packages/core/src/hooks/hook-file-config.ts
15472
15540
  */
15473
15541
  var ClineHooks = class ClineHooks extends ToolHooks {
15474
15542
  scriptsByEvent;
@@ -15623,6 +15691,11 @@ const CODEXCLI_CONVERTER_CONFIG = {
15623
15691
  numberPassthroughFields: [{
15624
15692
  canonical: "additionalContextLimit",
15625
15693
  tool: "additionalContextLimit"
15694
+ }],
15695
+ booleanPassthroughFields: [{
15696
+ canonical: "async",
15697
+ tool: "async",
15698
+ commandOnly: true
15626
15699
  }]
15627
15700
  };
15628
15701
  /**
@@ -16749,6 +16822,16 @@ const FACTORYDROID_CONVERTER_CONFIG = {
16749
16822
  subdividesGroup: true
16750
16823
  }]
16751
16824
  };
16825
+ /** Droid's nine event names, the keys a standalone `hooks.json` is made of. */
16826
+ const FACTORYDROID_EVENT_NAMES = new Set(Object.values(CANONICAL_TO_FACTORYDROID_EVENT_NAMES));
16827
+ /**
16828
+ * Whether a parsed hooks file is the standalone shape — keyed directly by event
16829
+ * name — rather than the `settings.json` shape that wraps the same map in a
16830
+ * `hooks` key.
16831
+ */
16832
+ function hasFactorydroidEventKey(parsed) {
16833
+ return Object.keys(parsed).some((key) => FACTORYDROID_EVENT_NAMES.has(key));
16834
+ }
16752
16835
  var FactorydroidHooks = class FactorydroidHooks extends ToolHooks {
16753
16836
  constructor(params) {
16754
16837
  super({
@@ -16776,14 +16859,6 @@ var FactorydroidHooks = class FactorydroidHooks extends ToolHooks {
16776
16859
  }
16777
16860
  static async fromRulesyncHooks({ outputRoot = process.cwd(), rulesyncHooks, validate = true, global = false, logger }) {
16778
16861
  const paths = FactorydroidHooks.getSettablePaths({ global });
16779
- const filePath = join(outputRoot, paths.relativeDirPath, paths.relativeFilePath);
16780
- const existingContent = await readFileContentOrNull(filePath) ?? JSON.stringify({}, null, 2);
16781
- let settings;
16782
- try {
16783
- settings = JSON.parse(existingContent);
16784
- } catch (error) {
16785
- throw new Error(`Failed to parse existing Factory Droid hooks file at ${filePath}: ${formatError(error)}`, { cause: error });
16786
- }
16787
16862
  const config = rulesyncHooks.getJson();
16788
16863
  const factorydroidHooks = canonicalToToolHooks({
16789
16864
  config,
@@ -16791,11 +16866,7 @@ var FactorydroidHooks = class FactorydroidHooks extends ToolHooks {
16791
16866
  converterConfig: FACTORYDROID_CONVERTER_CONFIG,
16792
16867
  logger
16793
16868
  });
16794
- const merged = {
16795
- ...settings,
16796
- hooks: factorydroidHooks
16797
- };
16798
- const fileContent = JSON.stringify(merged, null, 2);
16869
+ const fileContent = JSON.stringify(factorydroidHooks, null, 2);
16799
16870
  return new FactorydroidHooks({
16800
16871
  outputRoot,
16801
16872
  relativeDirPath: paths.relativeDirPath,
@@ -16805,14 +16876,14 @@ var FactorydroidHooks = class FactorydroidHooks extends ToolHooks {
16805
16876
  });
16806
16877
  }
16807
16878
  toRulesyncHooks({ logger } = {}) {
16808
- let settings;
16879
+ let parsed;
16809
16880
  try {
16810
- settings = JSON.parse(this.getFileContent());
16881
+ parsed = JSON.parse(this.getFileContent());
16811
16882
  } catch (error) {
16812
16883
  throw new Error(`Failed to parse Factory Droid hooks content in ${join(this.getRelativeDirPath(), this.getRelativeFilePath())}: ${formatError(error)}`, { cause: error });
16813
16884
  }
16814
16885
  const hooks = toolHooksToCanonical({
16815
- hooks: settings.hooks,
16886
+ hooks: hasFactorydroidEventKey(parsed) ? parsed : parsed.hooks,
16816
16887
  converterConfig: FACTORYDROID_CONVERTER_CONFIG,
16817
16888
  logger
16818
16889
  });
@@ -16947,18 +17018,7 @@ const GROKCLI_CONVERTER_CONFIG = {
16947
17018
  tool: "env",
16948
17019
  commandOnly: true
16949
17020
  }],
16950
- noMatcherEvents: /* @__PURE__ */ new Set([
16951
- "sessionStart",
16952
- "sessionEnd",
16953
- "beforeSubmitPrompt",
16954
- "stop",
16955
- "stopFailure",
16956
- "notification",
16957
- "subagentStart",
16958
- "subagentStop",
16959
- "preCompact",
16960
- "postCompact"
16961
- ])
17021
+ noMatcherEvents: /* @__PURE__ */ new Set(["stop", "beforeSubmitPrompt"])
16962
17022
  };
16963
17023
  /**
16964
17024
  * Hooks generator for Grok CLI (xAI Grok Build).
@@ -23139,6 +23199,78 @@ var CursorMcp = class CursorMcp extends ToolMcp {
23139
23199
  };
23140
23200
  //#endregion
23141
23201
  //#region src/features/mcp/deepagents-mcp.ts
23202
+ const TOOL_NAME = "deepagents";
23203
+ /**
23204
+ * Map a canonical transport onto the three dcode accepts.
23205
+ *
23206
+ * `_resolve_server_type` takes `stdio`, `sse` and `http`, plus the aliases
23207
+ * `streamable_http` / `streamable-http` → `http`. Rulesync's canonical
23208
+ * vocabulary is wider: `local` and `ws` are spellings dcode rejects outright,
23209
+ * and a rejected server is dropped at load time with only a log line. `local`
23210
+ * has an exact equivalent so it is translated; `ws` has none and is skipped at
23211
+ * generate time instead, where the warning can still reach the author.
23212
+ *
23213
+ * @see https://docs.langchain.com/oss/deepagents/code/mcp-tools
23214
+ */
23215
+ function normalizeDeepagentsTransport(transport) {
23216
+ switch (transport) {
23217
+ case "local":
23218
+ case "stdio": return "stdio";
23219
+ case "streamable-http":
23220
+ case "streamable_http":
23221
+ case "http": return "http";
23222
+ case "sse": return "sse";
23223
+ default: return;
23224
+ }
23225
+ }
23226
+ /**
23227
+ * Translate one canonical server into dcode's `.mcp.json` shape, or `null` to
23228
+ * skip it.
23229
+ *
23230
+ * Two upstream constraints from `_validate_tool_filter_fields` are enforced
23231
+ * here, because breaking either one makes dcode drop the whole server: the two
23232
+ * filters are mutually exclusive on a single server, and neither may be an
23233
+ * empty list.
23234
+ */
23235
+ function toDeepagentsServer({ name, server, logger }) {
23236
+ const rawTransport = server.transport ?? server.type;
23237
+ if (rawTransport === "ws") return warnAndSkipMcpServer({
23238
+ toolName: TOOL_NAME,
23239
+ serverName: name,
23240
+ reason: "the WebSocket transport, which deepagents does not support",
23241
+ logger
23242
+ });
23243
+ const { enabledTools, disabledTools, type: _type, transport, ...rest } = server;
23244
+ const converted = { ...rest };
23245
+ const normalized = normalizeDeepagentsTransport(rawTransport);
23246
+ if (normalized !== void 0) if (transport !== void 0) converted.transport = normalized;
23247
+ else converted.type = normalized;
23248
+ if (enabledTools !== void 0 && disabledTools !== void 0) return warnAndSkipMcpServer({
23249
+ toolName: TOOL_NAME,
23250
+ serverName: name,
23251
+ reason: "both enabledTools and disabledTools, which deepagents rejects — pick one",
23252
+ logger
23253
+ });
23254
+ if (enabledTools !== void 0) {
23255
+ if (enabledTools.length === 0) return warnAndSkipMcpServer({
23256
+ toolName: TOOL_NAME,
23257
+ serverName: name,
23258
+ reason: "an empty enabledTools list, which allows no tools at all and which deepagents rejects",
23259
+ logger
23260
+ });
23261
+ converted.allowedTools = enabledTools;
23262
+ } else if (disabledTools !== void 0) if (disabledTools.length === 0) logger?.warn(`${TOOL_NAME} MCP: dropping the empty disabledTools list on "${name}"; it denies nothing, and deepagents rejects the empty form.`);
23263
+ else converted.disabledTools = disabledTools;
23264
+ return converted;
23265
+ }
23266
+ /** Lift dcode's spellings back into the canonical model. */
23267
+ function toRulesyncServer(server) {
23268
+ const { allowedTools, ...rest } = server;
23269
+ const converted = { ...rest };
23270
+ for (const key of ["type", "transport"]) if (converted[key] === "streamable_http" || converted[key] === "streamable-http") converted[key] = "http";
23271
+ if (Array.isArray(allowedTools)) converted.enabledTools = allowedTools;
23272
+ return converted;
23273
+ }
23142
23274
  var DeepagentsMcp = class DeepagentsMcp extends ToolMcp {
23143
23275
  json;
23144
23276
  constructor(params) {
@@ -23173,12 +23305,22 @@ var DeepagentsMcp = class DeepagentsMcp extends ToolMcp {
23173
23305
  validate
23174
23306
  });
23175
23307
  }
23176
- static async fromRulesyncMcp({ outputRoot = process.cwd(), rulesyncMcp, validate = true, global = false }) {
23308
+ static async fromRulesyncMcp({ outputRoot = process.cwd(), rulesyncMcp, validate = true, global = false, logger }) {
23177
23309
  const paths = this.getSettablePaths({ global });
23178
23310
  const fileContent = await readFileContentOrNull(join(outputRoot, paths.relativeDirPath, paths.relativeFilePath)) ?? JSON.stringify({ mcpServers: {} }, null, 2);
23311
+ const json = JSON.parse(fileContent);
23312
+ const mcpServers = {};
23313
+ for (const [name, server] of Object.entries(rulesyncMcp.getMcpServers())) {
23314
+ const converted = toDeepagentsServer({
23315
+ name,
23316
+ server,
23317
+ logger
23318
+ });
23319
+ if (converted !== null) mcpServers[name] = converted;
23320
+ }
23179
23321
  const mcpJson = {
23180
- ...JSON.parse(fileContent),
23181
- mcpServers: rulesyncMcp.getMcpServers()
23322
+ ...json,
23323
+ mcpServers
23182
23324
  };
23183
23325
  return new DeepagentsMcp({
23184
23326
  outputRoot,
@@ -23189,7 +23331,9 @@ var DeepagentsMcp = class DeepagentsMcp extends ToolMcp {
23189
23331
  });
23190
23332
  }
23191
23333
  toRulesyncMcp() {
23192
- return this.toRulesyncMcpDefault({ fileContent: JSON.stringify({ mcpServers: this.json.mcpServers }, null, 2) });
23334
+ const servers = isRecord$1(this.json.mcpServers) ? this.json.mcpServers : {};
23335
+ const mcpServers = Object.fromEntries(Object.entries(servers).map(([name, server]) => [name, isRecord$1(server) ? toRulesyncServer(server) : server]));
23336
+ return this.toRulesyncMcpDefault({ fileContent: JSON.stringify({ mcpServers }, null, 2) });
23193
23337
  }
23194
23338
  validate() {
23195
23339
  return {
@@ -27204,8 +27348,8 @@ const toolMcpFactories = /* @__PURE__ */ new Map([
27204
27348
  meta: {
27205
27349
  supportsProject: true,
27206
27350
  supportsGlobal: true,
27207
- supportsEnabledTools: false,
27208
- supportsDisabledTools: false
27351
+ supportsEnabledTools: true,
27352
+ supportsDisabledTools: true
27209
27353
  }
27210
27354
  }],
27211
27355
  ["factorydroid", {
@@ -27214,7 +27358,7 @@ const toolMcpFactories = /* @__PURE__ */ new Map([
27214
27358
  supportsProject: true,
27215
27359
  supportsGlobal: true,
27216
27360
  supportsEnabledTools: false,
27217
- supportsDisabledTools: false
27361
+ supportsDisabledTools: true
27218
27362
  }
27219
27363
  }],
27220
27364
  ["goose", {
@@ -27340,7 +27484,7 @@ const toolMcpFactories = /* @__PURE__ */ new Map([
27340
27484
  supportsProject: true,
27341
27485
  supportsGlobal: false,
27342
27486
  supportsEnabledTools: false,
27343
- supportsDisabledTools: false
27487
+ supportsDisabledTools: true
27344
27488
  }
27345
27489
  }],
27346
27490
  ["zoocode", {
@@ -27349,7 +27493,7 @@ const toolMcpFactories = /* @__PURE__ */ new Map([
27349
27493
  supportsProject: true,
27350
27494
  supportsGlobal: false,
27351
27495
  supportsEnabledTools: false,
27352
- supportsDisabledTools: false
27496
+ supportsDisabledTools: true
27353
27497
  }
27354
27498
  }],
27355
27499
  ["rovodev", {
@@ -31276,6 +31420,7 @@ function convertGoosePermissionConfigToRulesync(userPermission) {
31276
31420
  const GROKCLI_UI_KEY = "ui";
31277
31421
  const GROKCLI_PERMISSION_MODE_KEY = "permission_mode";
31278
31422
  const GROKCLI_PERMISSION_KEY = "permission";
31423
+ const GROKCLI_AUTO_PERMISSION_MODE = "auto";
31279
31424
  const CATCH_ALL_PATTERN$2 = "*";
31280
31425
  const MCP_CANONICAL_PREFIX$1 = "mcp__";
31281
31426
  const CATEGORY_TO_GROK_TOOL = {
@@ -31489,8 +31634,9 @@ var GrokcliPermissions = class GrokcliPermissions extends ToolPermissions {
31489
31634
  deny: buckets.deny,
31490
31635
  ask: buckets.ask
31491
31636
  };
31492
- const uiPatch = global ? { [GROKCLI_UI_KEY]: {
31493
- ...isRecord$1(parsed[GROKCLI_UI_KEY]) ? parsed[GROKCLI_UI_KEY] : {},
31637
+ const existingUi = isRecord$1(parsed[GROKCLI_UI_KEY]) ? parsed[GROKCLI_UI_KEY] : {};
31638
+ const uiPatch = global && existingUi[GROKCLI_PERMISSION_MODE_KEY] !== GROKCLI_AUTO_PERMISSION_MODE ? { [GROKCLI_UI_KEY]: {
31639
+ ...existingUi,
31494
31640
  [GROKCLI_PERMISSION_MODE_KEY]: deriveGrokPermissionMode(config)
31495
31641
  } } : {};
31496
31642
  return new GrokcliPermissions({
@@ -36775,11 +36921,12 @@ var AmpSkill = class AmpSkill extends ToolSkill {
36775
36921
  };
36776
36922
  }
36777
36923
  toRulesyncSkill() {
36778
- const frontmatter = this.getFrontmatter();
36924
+ const { name, description, ...ampSection } = this.getFrontmatter();
36779
36925
  const rulesyncFrontmatter = {
36780
- name: frontmatter.name,
36781
- description: frontmatter.description,
36782
- targets: ["*"]
36926
+ name,
36927
+ description,
36928
+ targets: ["*"],
36929
+ ...Object.keys(ampSection).length > 0 && { amp: ampSection }
36783
36930
  };
36784
36931
  return new RulesyncSkill({
36785
36932
  outputRoot: this.outputRoot,
@@ -36796,6 +36943,7 @@ var AmpSkill = class AmpSkill extends ToolSkill {
36796
36943
  const settablePaths = AmpSkill.getSettablePaths({ global });
36797
36944
  const rulesyncFrontmatter = rulesyncSkill.getFrontmatter();
36798
36945
  const ampFrontmatter = {
36946
+ ...rulesyncFrontmatter.amp,
36799
36947
  name: rulesyncFrontmatter.name,
36800
36948
  description: rulesyncFrontmatter.description
36801
36949
  };
@@ -39222,6 +39370,47 @@ const JunieSkillFrontmatterSchema = z.looseObject({
39222
39370
  name: z.string(),
39223
39371
  description: z.string()
39224
39372
  });
39373
+ /** An ATX markdown heading line (`#` … `######`). */
39374
+ const HEADING_LINE = /^#{1,6}(\s|$)/;
39375
+ /**
39376
+ * Junie's own fallback for a `SKILL.md` with no `description`: "If
39377
+ * `description` is not provided in the frontmatter, Junie CLI extracts the
39378
+ * first paragraph of the body content as the description." Headings do not
39379
+ * count as that paragraph — "If the body is also empty or contains only
39380
+ * headings, the skill will fail to load."
39381
+ *
39382
+ * This is import-only. The canonical `RulesyncSkillFrontmatter` requires a
39383
+ * description, so without the fallback a skill Junie itself loads fine aborts
39384
+ * the whole import; generation keeps emitting an explicit description, which
39385
+ * the same docs recommend.
39386
+ *
39387
+ * Heading lines are therefore skipped rather than taken: a body opening with
39388
+ * `# Skill Name` would otherwise import that title as the description and —
39389
+ * because the next generate writes it out explicitly — replace Junie's own
39390
+ * correct fallback with the wrong value everywhere, canonical config included.
39391
+ *
39392
+ * A paragraph runs to the first blank line or heading, and is collapsed onto
39393
+ * one line because it becomes a YAML frontmatter value. A fenced code block is
39394
+ * not treated specially: it is ordinary content, so a body whose first
39395
+ * paragraph is a fence yields the fence text. Returns an empty string when the
39396
+ * body holds no such paragraph, which the caller turns into a skipped skill.
39397
+ *
39398
+ * @see https://junie.jetbrains.com/docs/agent-skills.html
39399
+ */
39400
+ function deriveDescriptionFromBody(body) {
39401
+ const paragraph = [];
39402
+ for (const rawLine of body.split(/\r?\n/)) {
39403
+ const line = rawLine.trim();
39404
+ if (paragraph.length === 0) {
39405
+ if (line === "" || HEADING_LINE.test(line)) continue;
39406
+ paragraph.push(line);
39407
+ continue;
39408
+ }
39409
+ if (line === "" || HEADING_LINE.test(line)) break;
39410
+ paragraph.push(line);
39411
+ }
39412
+ return paragraph.join(" ").trim();
39413
+ }
39225
39414
  /**
39226
39415
  * Represents a JetBrains Junie skill directory.
39227
39416
  * Skills are stored under the .junie/skills directory with SKILL.md files.
@@ -39318,7 +39507,16 @@ var JunieSkill = class JunieSkill extends ToolSkill {
39318
39507
  ...params,
39319
39508
  getSettablePaths: JunieSkill.getSettablePaths
39320
39509
  });
39321
- const result = JunieSkillFrontmatterSchema.safeParse(loaded.frontmatter);
39510
+ let frontmatter = loaded.frontmatter;
39511
+ if (isRecord$1(frontmatter) && frontmatter.description === void 0) {
39512
+ const derived = deriveDescriptionFromBody(loaded.body);
39513
+ if (derived === "") throw new Error(`Cannot import ${join(loaded.outputRoot, loaded.relativeDirPath, loaded.dirName, SKILL_FILE_NAME)}: it has no description and its body has no paragraph to derive one from, so Junie cannot load it either. Add a description to the frontmatter.`);
39514
+ frontmatter = {
39515
+ ...frontmatter,
39516
+ description: derived
39517
+ };
39518
+ }
39519
+ const result = JunieSkillFrontmatterSchema.safeParse(frontmatter);
39322
39520
  if (!result.success) {
39323
39521
  const skillDirPath = join(loaded.outputRoot, loaded.relativeDirPath, loaded.dirName);
39324
39522
  throw new Error(`Invalid frontmatter in ${join(skillDirPath, SKILL_FILE_NAME)}: ${formatError(result.error)}`);
@@ -41783,7 +41981,8 @@ const toolSkillFactories = /* @__PURE__ */ new Map([
41783
41981
  meta: {
41784
41982
  supportsProject: true,
41785
41983
  supportsSimulated: false,
41786
- supportsGlobal: true
41984
+ supportsGlobal: true,
41985
+ lenientImport: true
41787
41986
  }
41788
41987
  }],
41789
41988
  ["kilo", {
@@ -54722,4 +54921,4 @@ async function importChecksCore(params) {
54722
54921
  //#endregion
54723
54922
  export { JsonLogger as $, RULESYNC_PERMISSIONS_LEGACY_FILE_NAME as $t, RulesyncRuleFrontmatterSchema as A, ALL_TOOL_TARGETS_WITH_WILDCARD as At, RulesyncCheck as B, RULESYNC_CURATED_RULES_RELATIVE_DIR_PATH as Bt, CODEXCLI_DIR as C, removeTempDirectory as Ct, RulesyncSkill as D, writeFileBuffer as Dt, RulesyncSubagentFrontmatterSchema as E, toPosixPath as Et, getRulesyncSourceCandidates as F, RULESYNC_AIIGNORE_RELATIVE_FILE_PATH as Ft, SKILL_FILE_NAME as G, RULESYNC_IGNORE_RELATIVE_FILE_PATH as Gt, stringifyFrontmatter as H, RULESYNC_HOOKS_FILE_NAME as Ht, resolveRulesyncSourceWritePath as I, RULESYNC_CHECKS_RELATIVE_DIR_PATH as It, ConfigFileSchema as J, RULESYNC_MCP_LEGACY_FILE_NAME as Jt, ConfigResolver as K, RULESYNC_LOCAL_CONFIG_RELATIVE_FILE_PATH as Kt, parseJsonc as L, RULESYNC_COMMANDS_RELATIVE_DIR_PATH as Lt, RulesyncMcp as M, ToolTargetSchema as Mt, RulesyncIgnore as N, MAX_FILE_SIZE as Nt, RulesyncSkillFrontmatterSchema as O, writeFileContent as Ot, RulesyncHooks as P, RULESYNC_AIIGNORE_FILE_NAME as Pt, ConsoleLogger as Q, RULESYNC_PERMISSIONS_FILE_NAME as Qt, RulesyncCommand as R, RULESYNC_CONFIG_RELATIVE_FILE_PATH as Rt, CODEXCLI_BASH_RULES_FILE_NAME as S, removeFileStrict as St, RulesyncSubagent as T, runWithDirectoryRollback as Tt, loadYaml as U, RULESYNC_HOOKS_LEGACY_FILE_NAME as Ut, RulesyncCheckFrontmatterSchema as V, RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH as Vt, SHARED_USER_MANAGED_CONFIG_PATHS as W, RULESYNC_HOOKS_RELATIVE_FILE_PATH as Wt, SourceEntrySchema as X, RULESYNC_MCP_SCHEMA_URL as Xt, GITIGNORE_DESTINATION_KEY as Y, RULESYNC_MCP_RELATIVE_FILE_PATH as Yt, findControlCharacter as Z, RULESYNC_NPM_SOURCES_LOCK_RELATIVE_FILE_PATH as Zt, CLAUDECODE_LOCAL_RULE_FILE_NAME as _, readFileContent as _t, convertFromTool as a, RULESYNC_SOURCES_LOCK_RELATIVE_FILE_PATH as an, assertTreeContainsNoSymlinks as at, CLAUDECODE_SKILLS_DIR_PATH as b, removeDirectoryStrict as bt, SubagentsProcessor as c, ALL_FEATURES_WITH_WILDCARD as cn, createTempDirectory as ct, IgnoreProcessor as d, fileExists as dt, RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH as en, fallbackLogger as et, HooksProcessor as f, findFilesByGlobs as ft, CLAUDECODE_DIR as g, listDirectoryFiles as gt, QWENCODE_LOCAL_RULE_FILE_NAME as h, isSymlink as ht, getProcessorRegistryEntry as i, RULESYNC_SKILLS_RELATIVE_DIR_PATH as in, assertDirectoryIfExists as it, RulesyncPermissions as j, PACKAGING_TOOL_TARGETS as jt, RulesyncRule as k, ALL_TOOL_TARGETS as kt, SkillsProcessor as l, DEPRECATED_FEATURE_REPLACEMENTS as ln, directoryExists as lt, QWENCODE_DIR as m, getHomeDirectory as mt, checkRulesyncDirExists as n, RULESYNC_RELATIVE_DIR_PATH as nn, CLIError as nt, isPackagingToolTarget as o, RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH as on, assertWritablePathInsideRoot as ot, CommandsProcessor as p, getFileSize as pt, CONFLICTING_TARGET_PAIRS as q, RULESYNC_MCP_FILE_NAME as qt, generate as r, RULESYNC_RULES_RELATIVE_DIR_PATH as rn, ErrorCodes as rt, RulesProcessor as s, ALL_FEATURES as sn, checkPathTraversal as st, importFromTool as t, RULESYNC_PERMISSIONS_SCHEMA_URL as tn, warnOnConflictingFlags as tt, McpProcessor as u, formatError as un, ensureDir as ut, CLAUDECODE_MEMORIES_DIR_NAME as v, readFileContentOrNull as vt, getLocalSkillDirNames as w, resolvePath as wt, ChecksProcessor as x, removeFile as xt, CLAUDECODE_SETTINGS_LOCAL_FILE_NAME as y, removeDirectory as yt, RulesyncCommandFrontmatterSchema as z, RULESYNC_CONFIG_SCHEMA_URL as zt };
54724
54923
 
54725
- //# sourceMappingURL=import-Bgkf_jVN.js.map
54924
+ //# sourceMappingURL=import-zGCKgpdt.js.map