rulesync 16.35.0 → 16.36.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.
@@ -4053,24 +4053,60 @@ const CONTROL_CHARS = [
4053
4053
  const hasControlChars = (val) => CONTROL_CHARS.some((char) => val.includes(char));
4054
4054
  const safeString = z.pipe(z.string(), z.custom((val) => typeof val === "string" && !hasControlChars(val), "must not contain newline, carriage return, or NUL characters"));
4055
4055
  /**
4056
+ * All canonical hook types — the union of the `type` values accepted by every
4057
+ * supported tool:
4058
+ * - `command` — universal (Claude Code, Codex CLI, Qwen Code, Cursor, …)
4059
+ * - `prompt` — Claude Code, Codex CLI, Qwen Code, Copilot CLI, Cursor, Devin
4060
+ * - `http` — Claude Code, Qwen Code, Copilot CLI, Grok CLI
4061
+ * - `agent` — Claude Code, Codex CLI
4062
+ * - `mcp_tool` — Claude Code
4063
+ * - `function` — Qwen Code (internal Skill-system executor)
4064
+ */
4065
+ const HOOK_TYPES = [
4066
+ "command",
4067
+ "prompt",
4068
+ "http",
4069
+ "agent",
4070
+ "mcp_tool",
4071
+ "function"
4072
+ ];
4073
+ /**
4074
+ * One choice offered by a Kiro `confirm` prompt: `run` decides whether picking
4075
+ * it executes the hook command. The prompt text is written back into JSON,
4076
+ * never embedded in generated code, so it is a plain string.
4077
+ */
4078
+ const HookConfirmOptionSchema = z.looseObject({
4079
+ id: z.string(),
4080
+ label: z.string(),
4081
+ run: z.boolean()
4082
+ });
4083
+ /**
4084
+ * Kiro's confirmation prompt shown before a `Stop`-trigger command hook runs.
4085
+ * `confirmCommand` (optional, inside the block) names a command whose JSON
4086
+ * stdout drives the prompt at run time: `{ "skip": true }` suppresses it, a
4087
+ * replacement `question` / `options` object overrides the static prompt, and
4088
+ * on failure or timeout the static prompt is used. Stored verbatim: Kiro is
4089
+ * the only consumer, and it owns the shape.
4090
+ * https://kiro.dev/docs/hooks/
4091
+ */
4092
+ const HookConfirmSchema = z.looseObject({
4093
+ question: z.string(),
4094
+ options: z.array(HookConfirmOptionSchema),
4095
+ confirmCommand: z.optional(safeString)
4096
+ });
4097
+ /**
4056
4098
  * Canonical hook definition.
4057
4099
  * Used in .rulesync/hooks.jsonc and mapped to tool-specific formats.
4058
4100
  */
4059
4101
  const HookDefinitionSchema = z.looseObject({
4060
4102
  command: z.optional(safeString),
4061
- type: z.optional(z.enum([
4062
- "command",
4063
- "prompt",
4064
- "http",
4065
- "agent",
4066
- "mcp_tool",
4067
- "function"
4068
- ])),
4103
+ type: z.optional(z.enum(HOOK_TYPES)),
4069
4104
  url: z.optional(safeString),
4070
4105
  timeout: z.optional(z.number()),
4071
4106
  cacheTtl: z.optional(z.number().check(nonnegative())),
4072
4107
  matcher: z.optional(safeString),
4073
4108
  enabled: z.optional(z.boolean()),
4109
+ confirm: z.optional(HookConfirmSchema),
4074
4110
  prompt: z.optional(safeString),
4075
4111
  loop_limit: z.optional(z.nullable(z.number())),
4076
4112
  name: z.optional(safeString),
@@ -4348,9 +4384,13 @@ const AMP_HOOK_EVENTS = [
4348
4384
  * the platform the other one owns — see `generateClineHookScript` and
4349
4385
  * `generateClineHookPowerShellScript`.
4350
4386
  *
4351
- * `TaskResume` and `TaskCancel` have no canonical counterpart and stay
4352
- * unmapped rather than being approximated by `sessionEnd` / `stop`, whose
4353
- * semantics differ.
4387
+ * `TaskCancel` is the shape of canonical `stopCancelled` (as Grok CLI's
4388
+ * `StopCancelled` and Codex CLI's `Interrupt` are): both runtimes fire it
4389
+ * *instead of* `TaskComplete` when the run ends aborted — the VS Code
4390
+ * extension from `afterRun` when the result status is `aborted`, the SDK/CLI
4391
+ * as `agent_abort` — and neither lets the script cancel anything. `TaskResume`
4392
+ * has no canonical counterpart and stays unmapped rather than being
4393
+ * approximated by `sessionStart`, whose semantics differ.
4354
4394
  *
4355
4395
  * @see https://github.com/cline/cline/blob/main/apps/vscode/src/core/hooks/utils.ts
4356
4396
  * @see https://github.com/cline/cline/blob/main/sdk/packages/core/src/hooks/hook-file-config.ts
@@ -4364,7 +4404,8 @@ const CLINE_HOOK_EVENTS = [
4364
4404
  "preCompact",
4365
4405
  "notification",
4366
4406
  "taskCompleted",
4367
- "afterError"
4407
+ "afterError",
4408
+ "stopCancelled"
4368
4409
  ];
4369
4410
  /**
4370
4411
  * Hook events supported by GitHub Copilot (cloud coding agent).
@@ -4682,23 +4723,32 @@ const QWENCODE_HOOK_EVENTS = [
4682
4723
  * Hook events supported by Reasonix.
4683
4724
  *
4684
4725
  * Reasonix's `.reasonix/settings.json` (project) / `~/.reasonix/settings.json`
4685
- * (global) documents a ten-event surface (`PreToolUse`, `PostToolUse`,
4686
- * `UserPromptSubmit`, `Stop`, `PostLLMCall`, `SessionStart`, `SessionEnd`,
4687
- * `SubagentStop`, `Notification`, `PreCompact`). All ten have a clean canonical
4726
+ * (global) accepts the thirteen events declared in `internal/hook/hook.go`
4727
+ * (`var Events`): `PreToolUse`, `PostToolUse`, `PostToolUseFailure`,
4728
+ * `PermissionRequest`, `UserPromptSubmit`, `Stop`, `StopFailure`,
4729
+ * `PostLLMCall`, `SessionStart`, `SessionEnd`, `SubagentStop`, `Notification`,
4730
+ * `PreCompact`. The zh-CN desktop hooks page documents only ten of them, so the
4731
+ * source file is the authoritative list. All thirteen have a clean canonical
4688
4732
  * equivalent and are mapped: `PreToolUse`, `PostToolUse`,
4689
- * `UserPromptSubmit` ← `beforeSubmitPrompt`, `Stop`, `SessionStart`,
4690
- * `SessionEnd`, `SubagentStop`, `PostLLMCall` ← `postModelInvocation`,
4691
- * `Notification` ← `notification`, and `PreCompact` ← `preCompact`.
4692
- * `match` (Reasonix's matcher field name) is honored only on
4693
- * `PreToolUse`/`PostToolUse`, matching the canonical `matcher` field's
4694
- * tool-event scoping used by other adapters.
4733
+ * `PostToolUseFailure`, `PermissionRequest`, `UserPromptSubmit` ←
4734
+ * `beforeSubmitPrompt`, `Stop`, `StopFailure`, `SessionStart`, `SessionEnd`,
4735
+ * `SubagentStop`, `PostLLMCall` ← `postModelInvocation`, `Notification` ←
4736
+ * `notification`, and `PreCompact` ← `preCompact`.
4737
+ * `match` (Reasonix's matcher field name) is honored on the four tool-scoped
4738
+ * events (`PreToolUse`, `PostToolUse`, `PostToolUseFailure`,
4739
+ * `PermissionRequest` — `UsesToolMatcher` in `internal/hook/inspect.go`) and
4740
+ * ignored everywhere else.
4741
+ * @see https://github.com/esengine/DeepSeek-Reasonix/blob/main-v2/internal/hook/hook.go
4695
4742
  * @see https://github.com/esengine/DeepSeek-Reasonix/blob/main-v2/docs/DESKTOP_HOOKS.zh-CN.md
4696
4743
  */
4697
4744
  const REASONIX_HOOK_EVENTS = [
4698
4745
  "preToolUse",
4699
4746
  "postToolUse",
4747
+ "postToolUseFailure",
4748
+ "permissionRequest",
4700
4749
  "beforeSubmitPrompt",
4701
4750
  "stop",
4751
+ "stopFailure",
4702
4752
  "sessionStart",
4703
4753
  "sessionEnd",
4704
4754
  "subagentStop",
@@ -4744,8 +4794,8 @@ const GROKCLI_HOOK_EVENTS = [
4744
4794
  * added in 0.32.0 (`TurnStarted`, `UserPromptQueued`, `TaskStarted`,
4745
4795
  * `SessionHeartbeat`), none of which is mapped onto a canonical rulesync event
4746
4796
  * here. `Interrupt` (fires instead of `Stop` when the user interrupts a turn)
4747
- * does have a canonical shape — `stopCancelled`, which Grok CLI and Codex CLI
4748
- * map — but Kimi keeps it native-only for now, since an existing `kimi-code`
4797
+ * does have a canonical shape — `stopCancelled`, which Grok CLI, Codex CLI and
4798
+ * Cline map — but Kimi keeps it native-only for now, since an existing `kimi-code`
4749
4799
  * override addressing it by name would otherwise double up with a canonical
4750
4800
  * `stopCancelled` block; folding it in is a follow-up. All six are listed in
4751
4801
  * `KIMI_CODE_NATIVE_HOOK_EVENTS` so a per-tool `kimi-code` override can
@@ -5040,8 +5090,8 @@ const CONTINUE_TO_CANONICAL_EVENT_NAMES = Object.fromEntries(Object.entries(CANO
5040
5090
  /**
5041
5091
  * Hook events supported by Hermes Agent's native Shell Hooks system.
5042
5092
  *
5043
- * Hermes validates hook events against a fixed `VALID_HOOKS` set — 37 entries as
5044
- * of v0.20.2 (`v2026.8.16`); see {@link HERMESAGENT_NATIVE_HOOK_EVENTS} for the
5093
+ * Hermes validates hook events against a fixed `VALID_HOOKS` set — 39 entries as
5094
+ * of v0.21.3 (`v2026.9.14`); see {@link HERMESAGENT_NATIVE_HOOK_EVENTS} for the
5045
5095
  * full list. Only the events with a clean 1:1 canonical equivalent are mapped
5046
5096
  * here. All other native events round-trip through `hermesagent.hooks`.
5047
5097
  * @see https://github.com/NousResearch/hermes-agent/blob/main/website/docs/user-guide/features/hooks.md
@@ -5065,11 +5115,13 @@ const HERMESAGENT_HOOK_EVENTS = [
5065
5115
  * observers, `gateway_platform_event` and `transform_api_error_classification`.
5066
5116
  * Shell hooks are gated on the same set.
5067
5117
  *
5068
- * Verified against the tag `v2026.8.16` (v0.20.2): `VALID_HOOKS` holds 37
5069
- * entries, grown from 23 at `v2026.8.3` (v0.20.0). This list holds 36 of them —
5070
- * see {@link HERMESAGENT_SHELL_UNSUPPORTED_HOOK_EVENTS} for the exclusion.
5118
+ * Verified against the tag `v2026.9.14` (v0.21.3): `VALID_HOOKS` holds 39
5119
+ * entries, grown from 23 at `v2026.8.3` (v0.20.0) and 37 at `v2026.8.16`
5120
+ * (v0.20.2) — v0.21 added `agent_loop_stopped` and `on_room_member_activity`.
5121
+ * This list holds 38 of them — see
5122
+ * {@link HERMESAGENT_SHELL_UNSUPPORTED_HOOK_EVENTS} for the exclusion.
5071
5123
  *
5072
- * @see https://github.com/NousResearch/hermes-agent/blob/v2026.8.16/hermes_cli/plugins.py
5124
+ * @see https://github.com/NousResearch/hermes-agent/blob/v2026.9.14/hermes_cli/plugins.py
5073
5125
  */
5074
5126
  const HERMESAGENT_NATIVE_HOOK_EVENTS = [
5075
5127
  "pre_tool_call",
@@ -5095,8 +5147,10 @@ const HERMESAGENT_NATIVE_HOOK_EVENTS = [
5095
5147
  "subagent_start",
5096
5148
  "subagent_stop",
5097
5149
  "pre_gateway_dispatch",
5150
+ "agent_loop_stopped",
5098
5151
  "pre_approval_request",
5099
5152
  "post_approval_response",
5153
+ "on_room_member_activity",
5100
5154
  "pre_transcription",
5101
5155
  "kanban_task_claimed",
5102
5156
  "kanban_task_completed",
@@ -5119,8 +5173,8 @@ const HERMESAGENT_NATIVE_HOOK_EVENTS = [
5119
5173
  * rulesync only ever writes shell hooks, so these are excluded from
5120
5174
  * {@link HERMESAGENT_NATIVE_HOOK_EVENTS} and warned about with their own message.
5121
5175
  *
5122
- * @see https://github.com/NousResearch/hermes-agent/blob/v2026.8.16/hermes_cli/plugins.py — `SHELL_UNSUPPORTED_HOOKS`
5123
- * @see https://github.com/NousResearch/hermes-agent/blob/v2026.8.16/agent/shell_hooks.py — `_parse_hooks_block`
5176
+ * @see https://github.com/NousResearch/hermes-agent/blob/v2026.9.14/hermes_cli/plugins.py — `SHELL_UNSUPPORTED_HOOKS`
5177
+ * @see https://github.com/NousResearch/hermes-agent/blob/v2026.9.14/agent/shell_hooks.py — `_parse_hooks_block`
5124
5178
  */
5125
5179
  const HERMESAGENT_SHELL_UNSUPPORTED_HOOK_EVENTS = ["transform_api_error_classification"];
5126
5180
  /**
@@ -5421,7 +5475,8 @@ const CANONICAL_TO_CLINE_EVENT_NAMES = {
5421
5475
  preCompact: "PreCompact",
5422
5476
  notification: "Notification",
5423
5477
  taskCompleted: "TaskComplete",
5424
- afterError: "TaskError"
5478
+ afterError: "TaskError",
5479
+ stopCancelled: "TaskCancel"
5425
5480
  };
5426
5481
  /**
5427
5482
  * Map canonical camelCase event names to Copilot camelCase.
@@ -5739,8 +5794,11 @@ const QWENCODE_TO_CANONICAL_EVENT_NAMES = Object.fromEntries(Object.entries(CANO
5739
5794
  const CANONICAL_TO_REASONIX_EVENT_NAMES = {
5740
5795
  preToolUse: "PreToolUse",
5741
5796
  postToolUse: "PostToolUse",
5797
+ postToolUseFailure: "PostToolUseFailure",
5798
+ permissionRequest: "PermissionRequest",
5742
5799
  beforeSubmitPrompt: "UserPromptSubmit",
5743
5800
  stop: "Stop",
5801
+ stopFailure: "StopFailure",
5744
5802
  sessionStart: "SessionStart",
5745
5803
  sessionEnd: "SessionEnd",
5746
5804
  subagentStop: "SubagentStop",
@@ -7631,11 +7689,15 @@ const KiroPermissionsOverrideSchema = z.looseObject({
7631
7689
  });
7632
7690
  /**
7633
7691
  * Codex CLI's approval-workflow policy. Serialized as a kebab-case string in
7634
- * `.codex/config.toml`. `on-failure` is a legacy alias for `on-request` that
7635
- * Codex still accepts, so it is included so existing configs round-trip. The
7636
- * granular table form (`{ granular = { … } }`) is modeled separately in the
7637
- * override union.
7692
+ * `.codex/config.toml`. `on-failure` is a deprecated alias for `on-request`
7693
+ * that Codex still reads, so it stays accepted (generate warns) so existing
7694
+ * configs round-trip. `untrusted` was retired in Codex 0.149.0 and makes
7695
+ * Codex refuse to start; it stays in the enum only so an existing
7696
+ * `.rulesync/permissions.jsonc` still parses — generate warns and does not
7697
+ * write it, and import does not lift it. The granular table form
7698
+ * (`{ granular = { … } }`) is modeled separately in the override union.
7638
7699
  * @see https://learn.chatgpt.com/docs/config-file/config-reference
7700
+ * @see https://github.com/openai/codex/pull/39630
7639
7701
  */
7640
7702
  const CodexApprovalPolicySchema = z.enum([
7641
7703
  "untrusted",
@@ -7705,11 +7767,12 @@ const CodexBasePermissionProfileSchema = z.enum(CODEX_BASE_PERMISSION_PROFILES);
7705
7767
  * selected directly via `default_permissions` and skips the managed profile
7706
7768
  * entirely — canonical filesystem/network rules are ignored in that mode.
7707
7769
  * Defaults to `:workspace` when unspecified.
7708
- * - `approval_policy` — `untrusted` | `on-request` (legacy alias `on-failure`) |
7770
+ * - `approval_policy` — `on-request` (deprecated alias `on-failure`) |
7709
7771
  * `never`, or a `{ granular = { … } }` table (kept verbatim; the granular
7710
7772
  * schema has required fields that are brittle to model as typed keys).
7711
7773
  * Defaults to `on-request` when neither the override nor the existing
7712
- * config sets it.
7774
+ * config sets it. `untrusted` (retired in Codex 0.149.0) is still parsed
7775
+ * but skipped with a warning on generate, since Codex refuses to start on it.
7713
7776
  * - `sandbox_mode` — **deprecated.** `read-only` | `workspace-write` |
7714
7777
  * `danger-full-access`, with the sibling `sandbox_workspace_write` table
7715
7778
  * (`network_access`, `writable_roots`, …). Codex has superseded the classic
@@ -9595,7 +9658,8 @@ const RulesyncSkillFrontmatterSchema = z.looseObject({
9595
9658
  })),
9596
9659
  grokcli: z.optional(z.looseObject({
9597
9660
  "disable-model-invocation": z.optional(z.boolean()),
9598
- "user-invocable": z.optional(z.boolean())
9661
+ "user-invocable": z.optional(z.boolean()),
9662
+ paths: z.optional(z.union([z.string(), z.array(z.string())]))
9599
9663
  })),
9600
9664
  dsh: z.optional(z.looseObject({
9601
9665
  whenToUse: z.optional(z.string()),
@@ -9822,8 +9886,32 @@ var RulesyncSubagent = class RulesyncSubagent extends RulesyncFile {
9822
9886
  });
9823
9887
  }
9824
9888
  };
9825
- //#endregion
9826
- //#region src/features/skills/skills-utils.ts
9889
+ const SKILL_DESCRIPTION_MAX_LENGTH = 1024;
9890
+ const SKILL_NAME_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
9891
+ /**
9892
+ * Check a skill `name` against the length and character rules, phrased for the
9893
+ * `authority` that enforces them (e.g. "the Agent Skills spec" or "Zed"). When
9894
+ * that authority does something with a bad name beyond flagging it — Zed
9895
+ * refuses to load the skill — `consequence` names it so the warning says what
9896
+ * the author is about to lose. An empty name is the caller's concern: what it
9897
+ * means differs per consumer.
9898
+ */
9899
+ function collectSkillNameViolations({ name, authority, consequence }) {
9900
+ const violations = [];
9901
+ if (name.length > 64) violations.push(`\`name\` is ${name.length} characters; ${authority} allows at most 64${consequence === void 0 ? "" : ` and ${consequence}`}`);
9902
+ if (!SKILL_NAME_PATTERN.test(name)) violations.push(`\`name\` ${quoteForLog(name)} must contain only lowercase letters, digits and single hyphens, with no leading, trailing or consecutive hyphens${consequence === void 0 ? "" : `; ${authority} ${consequence}`}`);
9903
+ return violations;
9904
+ }
9905
+ /**
9906
+ * Report each violation as a warning prefixed with the skill file it concerns.
9907
+ * The path is shown POSIX-style and stripped of control characters so a hostile
9908
+ * directory name cannot forge log lines; `warnWithFallback` keeps the report
9909
+ * visible when the caller has no logger to hand.
9910
+ */
9911
+ function warnSkillViolations({ skillPath, violations, logger }) {
9912
+ const shownPath = stripControlCharacters(toPosixPath(skillPath));
9913
+ for (const violation of violations) warnWithFallback(logger, `${shownPath}: ${violation}`);
9914
+ }
9827
9915
  /**
9828
9916
  * Whether a directory on disk can be addressed by its name.
9829
9917
  *
@@ -10647,10 +10735,13 @@ const AmpCheckFrontmatterSchema = z.looseObject({
10647
10735
  /**
10648
10736
  * Represents an Amp code review check.
10649
10737
  *
10650
- * Amp natively reads code review checks as Markdown files with YAML frontmatter,
10651
- * scoped to the project (`.agents/checks/`) and user-wide (`~/.config/amp/checks/`).
10652
- * Each check runs as a per-check subagent during code review.
10653
- * @see https://ampcode.com/manual
10738
+ * Amp reads code review checks as Markdown files with YAML frontmatter,
10739
+ * scoped to the project (`.agents/checks/`) and user-wide (`~/.config/amp/checks/`;
10740
+ * Amp also reads `~/.config/agents/checks/`, which rulesync does not write).
10741
+ * Since the docs rebuild the supported runner is the official
10742
+ * `@amp/review-checks` plugin (`amp review` is deprecated); existing checks in
10743
+ * this format continue to apply.
10744
+ * @see https://ampcode.com/docs/review
10654
10745
  */
10655
10746
  var AmpCheck = class AmpCheck extends ToolCheck {
10656
10747
  frontmatter;
@@ -16890,10 +16981,8 @@ const AgentsSkillsSkillFrontmatterSchema = z.looseObject({
16890
16981
  metadata: z.optional(z.looseObject({})),
16891
16982
  "allowed-tools": z.optional(z.union([z.string(), z.array(z.string())]))
16892
16983
  });
16893
- const NAME_MAX_LENGTH = 64;
16894
- const DESCRIPTION_MAX_LENGTH = 1024;
16895
16984
  const COMPATIBILITY_MAX_LENGTH = 500;
16896
- const NAME_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
16985
+ const AGENT_SKILLS_SPEC = "the Agent Skills spec";
16897
16986
  /**
16898
16987
  * Placeholder for an object that has already been encoded once in the same
16899
16988
  * value. YAML anchors let one document reference the same node repeatedly, and
@@ -17057,12 +17146,14 @@ function collectAgentSkillViolations({ frontmatter, dirName, sourceAllowedTools
17057
17146
  const { name, description } = frontmatter;
17058
17147
  if (name.length === 0) violations.push("`name` is required and must not be empty");
17059
17148
  else {
17060
- if (name.length > NAME_MAX_LENGTH) violations.push(`\`name\` is ${name.length} characters; the Agent Skills spec allows at most ${NAME_MAX_LENGTH}`);
17061
- if (!NAME_PATTERN.test(name)) violations.push(`\`name\` "${name}" must contain only lowercase letters, digits and single hyphens, with no leading, trailing or consecutive hyphens`);
17149
+ violations.push(...collectSkillNameViolations({
17150
+ name,
17151
+ authority: AGENT_SKILLS_SPEC
17152
+ }));
17062
17153
  if (name !== dirName) violations.push(`\`name\` "${name}" must match its parent directory name "${dirName}"; conformant clients require them to be equal`);
17063
17154
  }
17064
17155
  if (description.length === 0) violations.push(EMPTY_SKILL_DESCRIPTION_VIOLATION);
17065
- else if (description.length > DESCRIPTION_MAX_LENGTH) violations.push(`\`description\` is ${description.length} characters; the Agent Skills spec allows at most ${DESCRIPTION_MAX_LENGTH}`);
17156
+ else if (description.length > 1024) violations.push(`\`description\` is ${description.length} characters; ${AGENT_SKILLS_SPEC} allows at most ${SKILL_DESCRIPTION_MAX_LENGTH}`);
17066
17157
  const { compatibility } = frontmatter;
17067
17158
  if (typeof compatibility !== "string" && compatibility !== void 0) violations.push("`compatibility` must be a string; the Agent Skills spec does not allow a mapping here");
17068
17159
  else if (compatibility !== void 0 && compatibility.length > COMPATIBILITY_MAX_LENGTH) violations.push(`\`compatibility\` is ${compatibility.length} characters; the Agent Skills spec allows at most ${COMPATIBILITY_MAX_LENGTH}`);
@@ -17180,16 +17271,19 @@ var AgentsSkillsSkill = class AgentsSkillsSkill extends ToolSkill {
17180
17271
  * directory rather than a same-named project path.
17181
17272
  */
17182
17273
  static reportSpecViolations({ outputRoot, relativeDirPath, dirName, frontmatter, sourceAllowedTools, logger }) {
17183
- const skillPath = agentSkillFilePath({
17184
- outputRoot,
17185
- relativeDirPath,
17186
- dirName
17274
+ warnSkillViolations({
17275
+ skillPath: agentSkillFilePath({
17276
+ outputRoot,
17277
+ relativeDirPath,
17278
+ dirName
17279
+ }),
17280
+ violations: collectAgentSkillViolations({
17281
+ frontmatter,
17282
+ dirName,
17283
+ sourceAllowedTools
17284
+ }),
17285
+ logger
17187
17286
  });
17188
- for (const violation of collectAgentSkillViolations({
17189
- frontmatter,
17190
- dirName,
17191
- sourceAllowedTools
17192
- })) warnWithFallback(logger, `${stripControlCharacters(toPosixPath(skillPath))}: ${violation}`);
17193
17287
  }
17194
17288
  static isTargetedByRulesyncSkill(rulesyncSkill) {
17195
17289
  const targets = rulesyncSkill.getFrontmatter().targets;
@@ -17829,6 +17923,15 @@ const KiloCommandFrontmatterSchema = z.looseObject({
17829
17923
  model: z.optional(z.string()),
17830
17924
  variant: z.optional(z.string())
17831
17925
  });
17926
+ /**
17927
+ * Command names Kilo keeps for itself. Its loader throws on a custom command
17928
+ * file named `goal.md` in either the project or the global directory ("The
17929
+ * /goal command is reserved for session goals. Rename the custom command."),
17930
+ * and a throw there aborts the whole command scan — every other custom
17931
+ * command becomes unreachable too, not only the reserved one.
17932
+ * https://github.com/Kilo-Org/kilocode/blob/v7.6.2/packages/opencode/src/command/index.ts
17933
+ */
17934
+ const KILO_RESERVED_COMMAND_NAMES = /* @__PURE__ */ new Set(["goal"]);
17832
17935
  var KiloCommand = class KiloCommand extends ToolCommand {
17833
17936
  frontmatter;
17834
17937
  body;
@@ -17871,6 +17974,20 @@ var KiloCommand = class KiloCommand extends ToolCommand {
17871
17974
  validate: true
17872
17975
  });
17873
17976
  }
17977
+ /**
17978
+ * Refuse to write a command Kilo reserves (see
17979
+ * {@link KILO_RESERVED_COMMAND_NAMES}). The name Kilo loads is the file
17980
+ * stem, and the processor calls this on the flattened path, so `goal.md`
17981
+ * is refused at any nesting depth under the basename naming and only at the
17982
+ * top level under the path naming, where `git/goal.md` becomes `git-goal`.
17983
+ * Import is untouched: a `goal.md` already in `.kilo/commands/` is read as
17984
+ * it is, since the reservation is Kilo's to enforce on its own files.
17985
+ */
17986
+ static getWriteBlockReason({ rulesyncCommand }) {
17987
+ const stem = basename(rulesyncCommand.getRelativeFilePath(), ".md");
17988
+ if (!KILO_RESERVED_COMMAND_NAMES.has(stem)) return null;
17989
+ return `Kilo reserves the /${stem} command for session goals and refuses to load a custom command by that name — it would abort Kilo's whole command scan, making every other custom command unreachable too. Rename the command, or exclude it from this target with \`targets\`.`;
17990
+ }
17874
17991
  static fromRulesyncCommand({ outputRoot = process.cwd(), rulesyncCommand, validate = true, global = false }) {
17875
17992
  const rulesyncFrontmatter = rulesyncCommand.getFrontmatter();
17876
17993
  const kiloFields = rulesyncFrontmatter.kilo ?? {};
@@ -20093,6 +20210,14 @@ var CommandsProcessor = class extends FeatureProcessor {
20093
20210
  if (!factory.class.isTargetedByRulesyncCommand(rulesyncCommand)) return null;
20094
20211
  const originalRelativePath = rulesyncCommand.getRelativeFilePath();
20095
20212
  const commandToConvert = factory.meta.supportsSubdirectory ? rulesyncCommand : this.flattenRelativeFilePath(rulesyncCommand);
20213
+ const writeBlockReason = factory.class.getWriteBlockReason?.({
20214
+ rulesyncCommand: commandToConvert,
20215
+ global: this.global
20216
+ });
20217
+ if (writeBlockReason !== void 0 && writeBlockReason !== null) {
20218
+ this.logger.warn(`Skipping command ${quoteForLog(originalRelativePath)} for '${this.toolTarget}': ${writeBlockReason}`);
20219
+ return null;
20220
+ }
20096
20221
  if (!factory.meta.supportsSubdirectory) {
20097
20222
  const flattenedPath = commandToConvert.getRelativeFilePath();
20098
20223
  const firstOrigin = flattenedPathOrigins.get(flattenedPath);
@@ -20962,6 +21087,72 @@ function isSupportedHookType({ type, converterConfig }) {
20962
21087
  return converterConfig.supportedHookTypes?.has(type ?? "command") ?? true;
20963
21088
  }
20964
21089
  /**
21090
+ * Whether a keyword field applies to this hook, warning about the reason when
21091
+ * it does not. Shared by both directions: the field is unusable on the tool
21092
+ * side either way, so the value is dropped rather than carried into a config
21093
+ * the tool would ignore or refuse.
21094
+ */
21095
+ function isKeywordFieldApplicable({ spec, hookType, eventName, direction, warn }) {
21096
+ const { canonical, tool, commandOnly, events } = spec;
21097
+ const field = direction === "generated" ? canonical : tool;
21098
+ if (!isFieldApplicable({
21099
+ commandOnly,
21100
+ hookType
21101
+ })) {
21102
+ warn?.(`Dropping "${field}" from a "${hookType}" hook on "${eventName}": this tool documents "${tool}" on "command" hooks only, so it is not ${direction}.`);
21103
+ return false;
21104
+ }
21105
+ if (events !== void 0 && !events.has(eventName)) {
21106
+ warn?.(`Dropping "${field}" from a "${hookType}" hook on "${eventName}": this tool honours "${tool}" on ${[...events].map((event) => `"${event}"`).join(", ")} only, so it is not ${direction}.`);
21107
+ return false;
21108
+ }
21109
+ return true;
21110
+ }
21111
+ /**
21112
+ * Emit the keyword passthrough fields: a canonical `true` becomes the tool's
21113
+ * keyword, while `false` is omitted as the tool's own default. A `true` the
21114
+ * tool would not honour on this hook is warned about, like the other kinds;
21115
+ * a `false` there is dropped silently, since omitting it changes nothing.
21116
+ */
21117
+ function emitKeywordFields({ def, hookType, eventName, fields, warn }) {
21118
+ const emitted = {};
21119
+ for (const spec of fields) {
21120
+ if (def[spec.canonical] !== true) continue;
21121
+ if (isKeywordFieldApplicable({
21122
+ spec,
21123
+ hookType,
21124
+ eventName,
21125
+ direction: "generated",
21126
+ warn
21127
+ })) emitted[spec.tool] = spec.trueValue;
21128
+ }
21129
+ return emitted;
21130
+ }
21131
+ /**
21132
+ * Import the keyword passthrough fields back into canonical booleans,
21133
+ * reversing {@link emitKeywordFields}. Only the two documented keywords are
21134
+ * read; anything else is warned about and dropped, so a typo in a hand-written
21135
+ * tool file never lands in `.rulesync/hooks.*` as a boolean it did not mean.
21136
+ */
21137
+ function importKeywordFields({ h, hookType, eventName, fields, warn }) {
21138
+ const imported = {};
21139
+ for (const spec of fields) {
21140
+ const value = h[spec.tool];
21141
+ if (value === void 0) continue;
21142
+ if (!isKeywordFieldApplicable({
21143
+ spec,
21144
+ hookType,
21145
+ eventName,
21146
+ direction: "imported",
21147
+ warn
21148
+ })) continue;
21149
+ if (value === spec.trueValue) imported[spec.canonical] = true;
21150
+ else if (spec.falseValue !== void 0 && value === spec.falseValue) imported[spec.canonical] = false;
21151
+ else warn?.(`Dropping "${spec.tool}" from an imported "${hookType}" hook on "${eventName}": ${quoteValueForWarning(value)} is not a value this tool documents for it.`);
21152
+ }
21153
+ return imported;
21154
+ }
21155
+ /**
20965
21156
  * Emit every per-hook passthrough kind for one canonical definition.
20966
21157
  */
20967
21158
  function emitAllPassthroughFields({ def, hookType, eventName, converterConfig, warn }) {
@@ -20974,6 +21165,13 @@ function emitAllPassthroughFields({ def, hookType, eventName, converterConfig, w
20974
21165
  isValid: isBooleanValue,
20975
21166
  warn
20976
21167
  }),
21168
+ ...emitKeywordFields({
21169
+ def,
21170
+ hookType,
21171
+ eventName,
21172
+ fields: converterConfig.keywordPassthroughFields ?? [],
21173
+ warn
21174
+ }),
20977
21175
  ...emitPassthroughFields({
20978
21176
  def,
20979
21177
  hookType,
@@ -21302,7 +21500,7 @@ function importTypePayloadFields({ h, hookType, warn }) {
21302
21500
  * Import every per-hook passthrough kind for one tool hook record, reversing
21303
21501
  * the emit side in {@link buildToolHooks}.
21304
21502
  */
21305
- function importAllPassthroughFields({ h, hookType, converterConfig, warn }) {
21503
+ function importAllPassthroughFields({ h, hookType, eventName, converterConfig, warn }) {
21306
21504
  return {
21307
21505
  ...importPassthroughFields({
21308
21506
  h,
@@ -21311,6 +21509,13 @@ function importAllPassthroughFields({ h, hookType, converterConfig, warn }) {
21311
21509
  isValid: isBooleanValue,
21312
21510
  warn
21313
21511
  }),
21512
+ ...importKeywordFields({
21513
+ h,
21514
+ hookType,
21515
+ eventName,
21516
+ fields: converterConfig.keywordPassthroughFields ?? [],
21517
+ warn
21518
+ }),
21314
21519
  ...importPassthroughFields({
21315
21520
  h,
21316
21521
  hookType,
@@ -21356,7 +21561,7 @@ function importAllPassthroughFields({ h, hookType, converterConfig, warn }) {
21356
21561
  /**
21357
21562
  * Convert a single tool hook record into a canonical hook definition.
21358
21563
  */
21359
- function toolHookToCanonical({ h, rawEntry, converterConfig, warn }) {
21564
+ function toolHookToCanonical({ h, rawEntry, eventName, converterConfig, warn }) {
21360
21565
  const hookType = resolveImportedHookType({
21361
21566
  value: h.type,
21362
21567
  converterConfig
@@ -21404,6 +21609,7 @@ function toolHookToCanonical({ h, rawEntry, converterConfig, warn }) {
21404
21609
  ...importAllPassthroughFields({
21405
21610
  h,
21406
21611
  hookType,
21612
+ eventName,
21407
21613
  converterConfig,
21408
21614
  warn
21409
21615
  }),
@@ -21490,7 +21696,7 @@ function describeHookSkipReason({ h, rawEntry, hookType, converterConfig }) {
21490
21696
  /**
21491
21697
  * Convert a single tool matcher entry into canonical hook definitions.
21492
21698
  */
21493
- function toolMatcherEntryToCanonical({ rawEntry, converterConfig, warn }) {
21699
+ function toolMatcherEntryToCanonical({ rawEntry, eventName, converterConfig, warn }) {
21494
21700
  const hookDefs = rawEntry.hooks ?? [];
21495
21701
  const groupSkipReason = describeGroupSkipReason({
21496
21702
  rawEntry,
@@ -21518,6 +21724,7 @@ function toolMatcherEntryToCanonical({ rawEntry, converterConfig, warn }) {
21518
21724
  definitions.push(toolHookToCanonical({
21519
21725
  h,
21520
21726
  rawEntry,
21727
+ eventName,
21521
21728
  converterConfig,
21522
21729
  warn
21523
21730
  }));
@@ -21565,6 +21772,7 @@ function toolHooksToCanonical({ hooks, converterConfig, logger }) {
21565
21772
  if (!isToolMatcherEntry(rawEntry)) continue;
21566
21773
  defs.push(...toolMatcherEntryToCanonical({
21567
21774
  rawEntry,
21775
+ eventName,
21568
21776
  converterConfig,
21569
21777
  warn
21570
21778
  }));
@@ -25185,7 +25393,15 @@ const GOOSE_CONVERTER_CONFIG = {
25185
25393
  toolToCanonicalEventNames: GOOSE_TO_CANONICAL_EVENT_NAMES,
25186
25394
  projectDirVar: "",
25187
25395
  supportedHookTypes: /* @__PURE__ */ new Set(["command"]),
25188
- wildcardMatcherMeansAll: true
25396
+ wildcardMatcherMeansAll: true,
25397
+ keywordPassthroughFields: [{
25398
+ canonical: "failClosed",
25399
+ tool: "on_failure",
25400
+ trueValue: "block",
25401
+ falseValue: "allow",
25402
+ commandOnly: true,
25403
+ events: /* @__PURE__ */ new Set(["preToolUse"])
25404
+ }]
25189
25405
  };
25190
25406
  /**
25191
25407
  * Represents a Goose lifecycle hooks file.
@@ -25494,7 +25710,7 @@ function canonicalToHermesHooks({ config, toolOverrideHooks, logger }) {
25494
25710
  for (const [nativeEvent, definitions] of Object.entries(toolOverrideHooks ?? {})) {
25495
25711
  if (HERMESAGENT_CANONICAL_EVENTS.has(nativeEvent)) continue;
25496
25712
  if (HERMESAGENT_SHELL_UNSUPPORTED_EVENTS.has(nativeEvent)) logger?.warn(`Hermes hook event "${nativeEvent}" is Python-plugin-only; Hermes Agent refuses it for shell hooks, so the generated entry will never run.`);
25497
- else if (!HERMESAGENT_NATIVE_EVENTS.has(nativeEvent)) logger?.warn(`Hermes hook event "${nativeEvent}" is not documented by Hermes Agent v0.20.2; preserving it for forward compatibility.`);
25713
+ else if (!HERMESAGENT_NATIVE_EVENTS.has(nativeEvent)) logger?.warn(`Hermes hook event "${nativeEvent}" is not documented by Hermes Agent v0.21.3; preserving it for forward compatibility.`);
25498
25714
  setHermesHookEntries({
25499
25715
  result,
25500
25716
  event: nativeEvent,
@@ -26374,7 +26590,8 @@ const KiroIdeHookEntrySchema = z.looseObject({
26374
26590
  matcher: z.optional(z.string()),
26375
26591
  action: z.optional(KiroIdeHookActionSchema),
26376
26592
  timeout: z.optional(z.number()),
26377
- enabled: z.optional(z.boolean())
26593
+ enabled: z.optional(z.boolean()),
26594
+ confirm: z.optional(HookConfirmSchema)
26378
26595
  });
26379
26596
  const KiroIdeHooksFileSchema = z.looseObject({
26380
26597
  version: z.optional(z.string()),
@@ -26413,7 +26630,8 @@ function buildKiroIdeEntriesForEvent(trigger, definitions) {
26413
26630
  ...def.matcher !== void 0 && def.matcher !== null && def.matcher !== "" && { matcher: def.matcher },
26414
26631
  action,
26415
26632
  ...def.timeout !== void 0 && def.timeout !== null && def.timeout >= 0 && { timeout: def.timeout },
26416
- enabled: def.enabled ?? true
26633
+ enabled: def.enabled ?? true,
26634
+ ...def.confirm !== void 0 && { confirm: def.confirm }
26417
26635
  });
26418
26636
  }
26419
26637
  return entries;
@@ -26455,6 +26673,14 @@ function canonicalToKiroIdeHooks(config) {
26455
26673
  }
26456
26674
  return entries;
26457
26675
  }
26676
+ /**
26677
+ * Copies the optional per-hook fields (`enabled`, `confirm`) back onto the
26678
+ * canonical definition.
26679
+ */
26680
+ function copyOptionalHookFields({ entry, def }) {
26681
+ if (entry.enabled === false) def.enabled = false;
26682
+ if (entry.confirm !== void 0 && entry.confirm !== null) def.confirm = omitPrototypePollutionKeysDeep(entry.confirm);
26683
+ }
26458
26684
  function kiroIdeHooksToCanonical(entries) {
26459
26685
  const canonical = {};
26460
26686
  for (const entry of entries) {
@@ -26478,7 +26704,10 @@ function kiroIdeHooksToCanonical(entries) {
26478
26704
  if (entry.description !== void 0 && entry.description !== null) def.description = entry.description;
26479
26705
  if (entry.matcher !== void 0 && entry.matcher !== null && entry.matcher !== "") def.matcher = entry.matcher;
26480
26706
  if (entry.timeout !== void 0 && entry.timeout !== null) def.timeout = entry.timeout;
26481
- if (entry.enabled === false) def.enabled = false;
26707
+ copyOptionalHookFields({
26708
+ entry,
26709
+ def
26710
+ });
26482
26711
  const list = lookupOwn({
26483
26712
  record: canonical,
26484
26713
  key: eventName
@@ -27485,10 +27714,16 @@ var QwencodeHooks = class QwencodeHooks extends ToolHooks {
27485
27714
  //#endregion
27486
27715
  //#region src/features/hooks/reasonix-hooks.ts
27487
27716
  /**
27488
- * Only PreToolUse/PostToolUse honor the `match` field (an anchored regex
27489
- * against the tool name); it is ignored on every other event.
27717
+ * Only the tool-scoped events honor the `match` field (an anchored regex
27718
+ * against the tool name); it is ignored on every other event. Mirrors
27719
+ * `UsesToolMatcher` in upstream `internal/hook/inspect.go`.
27490
27720
  */
27491
- const REASONIX_MATCHER_EVENTS = /* @__PURE__ */ new Set(["PreToolUse", "PostToolUse"]);
27721
+ const REASONIX_MATCHER_EVENTS = /* @__PURE__ */ new Set([
27722
+ "PreToolUse",
27723
+ "PostToolUse",
27724
+ "PostToolUseFailure",
27725
+ "PermissionRequest"
27726
+ ]);
27492
27727
  const SUPPORTED_REASONIX_EVENTS = new Set(REASONIX_HOOK_EVENTS);
27493
27728
  /**
27494
27729
  * Convert canonical hooks config to the Reasonix `hooks` object.
@@ -27570,9 +27805,11 @@ function reasonixHooksToCanonical(hooks) {
27570
27805
  * Reasonix hooks live in a Claude-Code-style but standalone JSON file —
27571
27806
  * `.reasonix/settings.json` (project) or `~/.reasonix/settings.json`
27572
27807
  * (global) — separate from the `[permissions]`/`[[plugins]]` TOML config.
27573
- * All ten upstream events have a clean canonical equivalent and are mapped:
27574
- * PreToolUse/PostToolUse/UserPromptSubmit/Stop plus SessionStart/SessionEnd/
27575
- * SubagentStop/PostLLMCall/Notification/PreCompact (see REASONIX_HOOK_EVENTS).
27808
+ * All thirteen upstream events have a clean canonical equivalent and are mapped:
27809
+ * PreToolUse/PostToolUse/PostToolUseFailure/PermissionRequest/UserPromptSubmit/
27810
+ * Stop/StopFailure plus SessionStart/SessionEnd/SubagentStop/PostLLMCall/
27811
+ * Notification/PreCompact (see REASONIX_HOOK_EVENTS).
27812
+ * @see https://github.com/esengine/DeepSeek-Reasonix/blob/main-v2/internal/hook/hook.go
27576
27813
  * @see https://github.com/esengine/DeepSeek-Reasonix/blob/main-v2/docs/DESKTOP_HOOKS.zh-CN.md
27577
27814
  */
27578
27815
  var ReasonixHooks = class ReasonixHooks extends ToolHooks {
@@ -28398,6 +28635,23 @@ const PER_HOOK_ENABLED_TARGETS = /* @__PURE__ */ new Set([
28398
28635
  "kiro-ide",
28399
28636
  "zcode"
28400
28637
  ]);
28638
+ /** The targets whose hooks format carries the Kiro `confirm` prompt. */
28639
+ const PER_HOOK_CONFIRM_TARGETS = /* @__PURE__ */ new Set(["kiro-cli", "kiro-ide"]);
28640
+ /**
28641
+ * Names the shared events that carry a hook whose canonical definition
28642
+ * satisfies `predicate`, excluding events the target skips altogether (those
28643
+ * are already reported as skipped and produce no output, so warning about them
28644
+ * again would contradict that message).
28645
+ */
28646
+ function emittedEventsWithHook(params) {
28647
+ const { factory, sharedHooks, effectiveHooks, predicate } = params;
28648
+ const skippedEvents = new Set(unsupportedEventNames({
28649
+ factory,
28650
+ sharedHooks,
28651
+ effectiveHooks
28652
+ }));
28653
+ return Object.entries(sharedHooks).filter(([event, defs]) => !skippedEvents.has(event) && defs.some((def) => predicate(def))).map(([event]) => event);
28654
+ }
28401
28655
  const toolHooksFactories = /* @__PURE__ */ new Map([
28402
28656
  ["amp", {
28403
28657
  class: AmpHooks,
@@ -28968,15 +29222,11 @@ var HooksProcessor = class extends FeatureProcessor {
28968
29222
  }
28969
29223
  for (const [hookType, events] of unsupportedTypeToEvents) this.logger.warn(`Skipped ${hookType}-type hook(s) for ${this.toolTarget} (not supported): ${Array.from(events).join(", ")}`);
28970
29224
  }
28971
- if (!PER_HOOK_ENABLED_TARGETS.has(this.toolTarget)) {
28972
- const skippedEvents = new Set(unsupportedEventNames({
28973
- factory,
28974
- sharedHooks,
28975
- effectiveHooks
28976
- }));
28977
- const eventsWithDisabledHooks = Object.entries(sharedHooks).filter(([event, defs]) => !skippedEvents.has(event) && defs.some((def) => def.enabled === false)).map(([event]) => event);
28978
- if (eventsWithDisabledHooks.length > 0) this.logger.warn(`Emitting "enabled: false" hook(s) as active for ${this.toolTarget} (only the kiro-cli / kiro-ide / zcode hooks formats support the flag): ${eventsWithDisabledHooks.join(", ")}`);
28979
- }
29225
+ this.warnAboutDroppedPerHookFields({
29226
+ factory,
29227
+ sharedHooks,
29228
+ effectiveHooks
29229
+ });
28980
29230
  const eventsWithUnsupportedMatcher = unsupportedMatcherEventNames({
28981
29231
  factory,
28982
29232
  effectiveHooks
@@ -29006,6 +29256,33 @@ var HooksProcessor = class extends FeatureProcessor {
29006
29256
  if (auxiliaryFiles && auxiliaryFiles.length > 0) result.push(...auxiliaryFiles);
29007
29257
  return result;
29008
29258
  }
29259
+ /**
29260
+ * Warns about canonical per-hook fields that this target's hooks format
29261
+ * cannot express, so a hook does not silently change behavior on generation.
29262
+ * Only canonical definitions are considered: a tool-native field inside an
29263
+ * override block is passed through verbatim and honored by that tool.
29264
+ */
29265
+ warnAboutDroppedPerHookFields(params) {
29266
+ const { factory, sharedHooks, effectiveHooks } = params;
29267
+ if (!PER_HOOK_ENABLED_TARGETS.has(this.toolTarget)) {
29268
+ const eventsWithDisabledHooks = emittedEventsWithHook({
29269
+ factory,
29270
+ sharedHooks,
29271
+ effectiveHooks,
29272
+ predicate: (def) => def.enabled === false
29273
+ });
29274
+ if (eventsWithDisabledHooks.length > 0) this.logger.warn(`Emitting "enabled: false" hook(s) as active for ${this.toolTarget} (only the kiro-cli / kiro-ide / zcode hooks formats support the flag): ${eventsWithDisabledHooks.join(", ")}`);
29275
+ }
29276
+ if (!PER_HOOK_CONFIRM_TARGETS.has(this.toolTarget)) {
29277
+ const eventsWithConfirmHooks = emittedEventsWithHook({
29278
+ factory,
29279
+ sharedHooks,
29280
+ effectiveHooks,
29281
+ predicate: (def) => def.confirm !== void 0
29282
+ });
29283
+ if (eventsWithConfirmHooks.length > 0) this.logger.warn(`Emitting hook(s) without their "confirm" prompt for ${this.toolTarget} (only the kiro-cli / kiro-ide hooks formats support it): ${eventsWithConfirmHooks.join(", ")}`);
29284
+ }
29285
+ }
29009
29286
  async convertToolFilesToRulesyncFiles(toolFiles) {
29010
29287
  const hooks = toolFiles.filter((f) => f instanceof ToolHooks);
29011
29288
  const logger = withToolTargetPrefix({
@@ -34807,28 +35084,60 @@ function resolveHermesTimeout(config) {
34807
35084
  if (typeof config.networkTimeout === "number") return config.networkTimeout;
34808
35085
  }
34809
35086
  /**
34810
- * Copies the advanced Hermes-recognized per-server fields that have no canonical
34811
- * alias — `auth` (`oauth` for OAuth 2.1/PKCE), mTLS `client_cert` (string PEM
34812
- * path, or `[cert, key]`/`[cert, key, password]` list) and `client_key`,
34813
- * `connect_timeout` (seconds), `supports_parallel_tool_calls`,
34814
- * `keepalive_interval`, `elicitation`, `trust`, and `identity_header` — verbatim
34815
- * from `source` to `target`. Field names are identical on both sides (the
34816
- * canonical `McpServerSchema` is a `looseObject`), so this serves export and
34817
- * import alike. See the Hermes mcp-config-reference.
35087
+ * String-valued `oauth` keys Hermes reads (`tools/mcp_oauth.py`,
35088
+ * `tools/mcp_oauth_device.py`, v0.21.3): the callback overrides, the
35089
+ * pre-registered client, the CIMD document override, the DCR registration
35090
+ * fields, the token-request `user_agent`, the login `flow` (`device` for
35091
+ * RFC 8628) and the space-separated `scope` string sent on registration and
35092
+ * device authorization.
35093
+ */
35094
+ const HERMES_OAUTH_STRING_KEYS = [
35095
+ "redirect_uri",
35096
+ "redirect_host",
35097
+ "client_id",
35098
+ "client_secret",
35099
+ "client_name",
35100
+ "client_metadata_url",
35101
+ "token_endpoint_auth_method",
35102
+ "application_type",
35103
+ "user_agent",
35104
+ "flow",
35105
+ "scope"
35106
+ ];
35107
+ /**
35108
+ * Copies the `oauth` mapping of a server, keeping only the keys Hermes reads.
35109
+ *
35110
+ * Hermes reads a single `scope` string, never a `scopes` list; earlier Rulesync
35111
+ * versions wrote `scopes` regardless, which Hermes silently ignored. A `scopes`
35112
+ * list is therefore folded into `scope` (space-separated, the OAuth wire form)
35113
+ * when no `scope` is given, in both directions, so an authored list finally
35114
+ * reaches the authorization server and an old `config.yaml` imports cleanly.
34818
35115
  */
34819
35116
  function copyHermesOauth(source) {
34820
35117
  if (!isRecord$1(source)) return;
34821
35118
  const oauth = {};
34822
- for (const key of [
34823
- "redirect_uri",
34824
- "redirect_host",
34825
- "client_id",
34826
- "client_secret"
34827
- ]) if (typeof source[key] === "string") oauth[key] = source[key];
34828
- if (typeof source.redirect_port === "number") oauth.redirect_port = source.redirect_port;
34829
- if (isStringArray$2(source.scopes)) oauth.scopes = source.scopes;
35119
+ for (const key of HERMES_OAUTH_STRING_KEYS) if (typeof source[key] === "string") oauth[key] = source[key];
35120
+ if (typeof oauth.scope !== "string" && isStringArray$2(source.scopes)) oauth.scope = source.scopes.join(" ");
35121
+ for (const key of ["redirect_port", "timeout"]) if (typeof source[key] === "number") oauth[key] = source[key];
35122
+ if (typeof source.cimd === "boolean") oauth.cimd = source.cimd;
34830
35123
  return Object.keys(oauth).length > 0 ? oauth : void 0;
34831
35124
  }
35125
+ const HERMES_OPAQUE_MAPPING_KEYS = [
35126
+ "sampling",
35127
+ "elicitation",
35128
+ "identity_header",
35129
+ "lifecycle"
35130
+ ];
35131
+ /**
35132
+ * Copies the advanced Hermes-recognized per-server fields that have no canonical
35133
+ * alias — `auth` (`oauth` for OAuth 2.1/PKCE), mTLS `client_cert` (string PEM
35134
+ * path, or `[cert, key]`/`[cert, key, password]` list) and `client_key`,
35135
+ * `connect_timeout` (seconds), `supports_parallel_tool_calls`, `protocol`,
35136
+ * `lazy`, `keepalive_interval`, `elicitation`, `trust`, and `identity_header` —
35137
+ * verbatim from `source` to `target`. Field names are identical on both sides
35138
+ * (the canonical `McpServerSchema` is a `looseObject`), so this serves export
35139
+ * and import alike. See the Hermes mcp-config-reference.
35140
+ */
34832
35141
  function copyHermesAdvancedFields(source, target) {
34833
35142
  let copied = false;
34834
35143
  if (typeof source.auth === "string") {
@@ -34860,6 +35169,14 @@ function copyHermesAdvancedFields(source, target) {
34860
35169
  target[key] = source[key];
34861
35170
  copied = true;
34862
35171
  }
35172
+ if (typeof source.protocol === "string") {
35173
+ target.protocol = source.protocol;
35174
+ copied = true;
35175
+ }
35176
+ if (typeof source.lazy === "boolean") {
35177
+ target.lazy = source.lazy;
35178
+ copied = true;
35179
+ }
34863
35180
  if (typeof source.ssl_verify === "boolean" || typeof source.ssl_verify === "string") {
34864
35181
  target.ssl_verify = source.ssl_verify;
34865
35182
  copied = true;
@@ -34868,25 +35185,20 @@ function copyHermesAdvancedFields(source, target) {
34868
35185
  target.skip_preflight = source.skip_preflight;
34869
35186
  copied = true;
34870
35187
  }
34871
- if (isPlainObject$1(source.sampling)) {
34872
- target.sampling = omitPrototypePollutionKeys(structuredClone(source.sampling));
34873
- copied = true;
34874
- }
34875
35188
  if (typeof source.keepalive_interval === "number") {
34876
35189
  target.keepalive_interval = source.keepalive_interval;
34877
35190
  copied = true;
34878
35191
  }
34879
- if (isPlainObject$1(source.elicitation)) {
34880
- target.elicitation = omitPrototypePollutionKeys(structuredClone(source.elicitation));
34881
- copied = true;
34882
- }
34883
35192
  if (typeof source.trust === "string") {
34884
35193
  target.trust = source.trust;
34885
35194
  copied = true;
34886
35195
  }
34887
- if (isPlainObject$1(source.identity_header)) {
34888
- target.identity_header = omitPrototypePollutionKeys(structuredClone(source.identity_header));
34889
- copied = true;
35196
+ for (const key of HERMES_OPAQUE_MAPPING_KEYS) {
35197
+ const mapping = source[key];
35198
+ if (isPlainObject$1(mapping)) {
35199
+ target[key] = omitPrototypePollutionKeys(structuredClone(mapping));
35200
+ copied = true;
35201
+ }
34890
35202
  }
34891
35203
  return copied;
34892
35204
  }
@@ -35670,6 +35982,7 @@ function toKimiCodeServer({ name, server, logger }) {
35670
35982
  "headers",
35671
35983
  "bearerTokenEnvVar",
35672
35984
  "enabled",
35985
+ "deferred",
35673
35986
  "startupTimeoutMs",
35674
35987
  "toolTimeoutMs",
35675
35988
  "enabledTools",
@@ -36327,7 +36640,12 @@ const OpencodeMcpRemoteServerSchema = z.looseObject({
36327
36640
  headers: z.optional(z.record(z.string(), z.string())),
36328
36641
  enabled: z._default(z.boolean(), true)
36329
36642
  });
36330
- const OPENCODE_PASSTHROUGH_SERVER_FIELDS = ["timeout", "oauth"];
36643
+ const OPENCODE_PASSTHROUGH_SERVER_FIELDS = [
36644
+ "timeout",
36645
+ "oauth",
36646
+ "protocol",
36647
+ "codemode"
36648
+ ];
36331
36649
  const OPENCODE_MCP_TRANSPORT_KEYS = [
36332
36650
  ...Object.keys(OpencodeMcpLocalServerSchema.def.shape),
36333
36651
  ...Object.keys(OpencodeMcpRemoteServerSchema.def.shape),
@@ -36469,7 +36787,7 @@ function warnAboutToggleDroppedKeys(serverName, serverConfig, logger) {
36469
36787
  * - env -> environment
36470
36788
  * - disabled -> enabled (inverted)
36471
36789
  * - enabledTools/disabledTools -> top-level tools map (with server name prefix)
36472
- * - OpenCode-supported extras (timeout, oauth) -> passed through verbatim
36790
+ * - OpenCode-supported extras (timeout, oauth, protocol, codemode) -> passed through verbatim
36473
36791
  */
36474
36792
  function convertServerToOpencodeFormat(serverName, serverConfig, existingEntry, logger) {
36475
36793
  const serverRecord = serverConfig;
@@ -39462,7 +39780,7 @@ var ToolPermissions = class extends ToolFile {
39462
39780
  * builtin of that name). It lives in the shared Amp settings file:
39463
39781
  * `.amp/settings.json` (project) and `~/.config/amp/settings.json` (global).
39464
39782
  *
39465
- * Reference: https://ampcode.com/manual ("amp.tools.disable").
39783
+ * Reference: https://ampcode.com/docs/cli/settings ("amp.tools.disable").
39466
39784
  */
39467
39785
  const AMP_TOOLS_DISABLE_KEY = "amp.tools.disable";
39468
39786
  /**
@@ -39473,19 +39791,26 @@ const AMP_TOOLS_DISABLE_KEY = "amp.tools.disable";
39473
39791
  * glob. Rules are evaluated **first-match-wins**. It lives in the same shared
39474
39792
  * Amp settings file as `amp.tools.disable`.
39475
39793
  *
39476
- * `amp.permissions` is Amp's documented legacy/backwards-compat surface — it
39477
- * remains functional and is the only place to express `allow`/`ask` and
39478
- * argument-specific `reject` rules (the simpler `amp.tools.disable` array can
39479
- * only disable whole tools).
39794
+ * `amp.permissions` is Amp's legacy/backwards-compat surface — it remains
39795
+ * functional in the shipped CLI and is the only place to express `allow`/`ask`
39796
+ * and argument-specific `reject` rules (the simpler `amp.tools.disable` array
39797
+ * can only disable whole tools). The web docs no longer describe it (the
39798
+ * permissions section only points at custom plugins), but the shipped CLI
39799
+ * still lists it in its built-in settings reference (`amp --help`) and
39800
+ * evaluates it through `amp permissions list|test`.
39480
39801
  *
39481
- * Reference: https://ampcode.com/manual ("amp.permissions").
39802
+ * Reference: https://ampcode.com/docs/tools#permissions and
39803
+ * https://ampcode.com/docs/customize/plugins#example-plugin-permissions.
39482
39804
  */
39483
39805
  const AMP_PERMISSIONS_KEY = "amp.permissions";
39484
39806
  /**
39485
39807
  * The `amp.guardedFiles.allowlist` array (file globs allowed without
39486
39808
  * confirmation), `amp.dangerouslyAllowAll` boolean (disable all confirmation),
39487
39809
  * and `amp.mcpPermissions` array — sibling settings authored through the `amp`
39488
- * permissions override. Reference: https://ampcode.com/manual.
39810
+ * permissions override. Only `amp.mcpPermissions` is still on the web docs
39811
+ * (https://ampcode.com/docs/cli/settings); the other two, like
39812
+ * `amp.permissions`, survive only in the CLI's built-in settings reference
39813
+ * (see `AMP_PERMISSIONS_KEY`).
39489
39814
  */
39490
39815
  const AMP_GUARDED_FILES_ALLOWLIST_KEY = "amp.guardedFiles.allowlist";
39491
39816
  const AMP_DANGEROUSLY_ALLOW_ALL_KEY = "amp.dangerouslyAllowAll";
@@ -42814,6 +43139,8 @@ const CODEX_DANGER_FULL_ACCESS_BASELINE = ":danger-full-access";
42814
43139
  const CODEX_EXTENDABLE_BASELINES = new Set(CODEX_EXTENDABLE_BASELINE_PROFILES);
42815
43140
  const CODEX_DEFAULT_APPROVAL_POLICY = "on-request";
42816
43141
  const CODEX_DEFAULT_APPROVALS_REVIEWER = "auto_review";
43142
+ const CODEX_RETIRED_APPROVAL_POLICY = "untrusted";
43143
+ const CODEX_DEPRECATED_APPROVAL_POLICY = "on-failure";
42817
43144
  const CODEX_GLOB_SCAN_MAX_DEPTH = 8;
42818
43145
  const CODEX_MINIMAL_KEY = ":minimal";
42819
43146
  const CODEX_GIT_WRITE_RULES = { ".git/**": "write" };
@@ -42937,6 +43264,10 @@ var CodexcliPermissions = class CodexcliPermissions extends ToolPermissions {
42937
43264
  domainsHadUnknown
42938
43265
  });
42939
43266
  const override = extractCodexcliOverride(table);
43267
+ if (override.approval_policy === CODEX_RETIRED_APPROVAL_POLICY) {
43268
+ warnWithFallback(void 0, `${join(this.getRelativeDirPath(), this.getRelativeFilePath())} sets approval_policy = "${CODEX_RETIRED_APPROVAL_POLICY}", which was retired in Codex 0.149.0 and makes Codex refuse to start. It was not imported; remove it from the file, or mark the project untrusted in your user config instead ([projects."<path>"] trust_level = "${CODEX_RETIRED_APPROVAL_POLICY}").`);
43269
+ delete override.approval_policy;
43270
+ }
42940
43271
  if (typeof profile?.extends === "string" && CODEX_EXTENDABLE_BASELINES.has(profile.extends)) override.base_permission_profile = profile.extends;
42941
43272
  if (defaultProfile === CODEX_DANGER_FULL_ACCESS_BASELINE) override.base_permission_profile = CODEX_DANGER_FULL_ACCESS_BASELINE;
42942
43273
  const result = Object.keys(override).length > 0 ? {
@@ -43225,6 +43556,10 @@ function computeCodexcliOverridePatch({ existing, override, logger }) {
43225
43556
  continue;
43226
43557
  }
43227
43558
  if (value === void 0) continue;
43559
+ if (key === "approval_policy" && !isWritableApprovalPolicy({
43560
+ value,
43561
+ logger
43562
+ })) continue;
43228
43563
  if (key === "sandbox_mode" || key === "sandbox_workspace_write") logger?.warn(`Codex CLI permission override key "${key}" is deprecated. Codex prioritizes the legacy sandbox settings over permission profiles when both are present, so it disables the generated "${RULESYNC_PROFILE_NAME}" permissions profile. Use "base_permission_profile" and the shared "permission" block instead.`);
43229
43564
  const existingValue = existing[key];
43230
43565
  patch[key] = isPlainObject(existingValue) && isPlainObject(value) ? {
@@ -43232,12 +43567,37 @@ function computeCodexcliOverridePatch({ existing, override, logger }) {
43232
43567
  ...value
43233
43568
  } : value;
43234
43569
  }
43570
+ fillCodexcliDefaults({
43571
+ existing,
43572
+ patch,
43573
+ logger
43574
+ });
43575
+ return patch;
43576
+ }
43577
+ function fillCodexcliDefaults({ existing, patch, logger }) {
43235
43578
  const defaults = {
43236
43579
  approval_policy: CODEX_DEFAULT_APPROVAL_POLICY,
43237
43580
  approvals_reviewer: CODEX_DEFAULT_APPROVALS_REVIEWER
43238
43581
  };
43239
- for (const [key, value] of Object.entries(defaults)) if (patch[key] === void 0 && existing[key] === void 0) patch[key] = value;
43240
- return patch;
43582
+ for (const [key, value] of Object.entries(defaults)) {
43583
+ if (patch[key] !== void 0) continue;
43584
+ if (existing[key] === void 0) {
43585
+ patch[key] = value;
43586
+ continue;
43587
+ }
43588
+ if (key === "approval_policy" && existing[key] === CODEX_RETIRED_APPROVAL_POLICY) {
43589
+ logger?.warn(`The existing Codex CLI config.toml sets approval_policy = "${CODEX_RETIRED_APPROVAL_POLICY}", which was retired in Codex 0.149.0 and makes Codex refuse to start, so it was replaced with "${value}". To keep the strict behavior, mark the project untrusted in your user config instead ([projects."<path>"] trust_level = "${CODEX_RETIRED_APPROVAL_POLICY}").`);
43590
+ patch[key] = value;
43591
+ }
43592
+ }
43593
+ }
43594
+ function isWritableApprovalPolicy({ value, logger }) {
43595
+ if (value === CODEX_RETIRED_APPROVAL_POLICY) {
43596
+ logger?.warn(`Codex CLI permission override "approval_policy": "${CODEX_RETIRED_APPROVAL_POLICY}" was retired in Codex 0.149.0 and makes Codex refuse to start, so it was not written. Remove it from the override; to keep the strict behavior, mark the project untrusted in your user config instead ([projects."<path>"] trust_level = "${CODEX_RETIRED_APPROVAL_POLICY}").`);
43597
+ return false;
43598
+ }
43599
+ if (value === CODEX_DEPRECATED_APPROVAL_POLICY) logger?.warn(`Codex CLI permission override "approval_policy": "${CODEX_DEPRECATED_APPROVAL_POLICY}" is deprecated; Codex reads it as "${CODEX_DEFAULT_APPROVAL_POLICY}". Use "${CODEX_DEFAULT_APPROVAL_POLICY}" instead.`);
43600
+ return true;
43241
43601
  }
43242
43602
  function extractCodexcliOverride(table) {
43243
43603
  const override = {};
@@ -51689,6 +52049,18 @@ const TAKT_RUNTIME_TARGETS_KEY = "targets";
51689
52049
  const TAKT_RUNTIME_AUTO_ROUTING_KEY = "auto_routing";
51690
52050
  const TAKT_RUNTIME_PROFILE_KEY = "profile";
51691
52051
  const TAKT_RUNTIME_OPTIONS_KEY = "options";
52052
+ const TAKT_RUNTIME_ASSIGNMENTS_KEY = "assignments";
52053
+ const TAKT_RUNTIME_LADDER_KEY = "ladder";
52054
+ const TAKT_RUNTIME_EXTENDS_KEY = "extends";
52055
+ const TAKT_RUNTIME_ROUTER_PROFILE_KEY = "router_profile";
52056
+ const TAKT_RUNTIME_POOLS_KEY = "pools";
52057
+ const TAKT_RUNTIME_CANDIDATES_KEY = "candidates";
52058
+ const TAKT_RUNTIME_FALLBACK_PROFILE_KEY = "fallback_profile";
52059
+ /** The one `targets` map that routes companion reviewers rather than agents. */
52060
+ const TAKT_RUNTIME_COMPANIONS_TARGET_KEY = "companions";
52061
+ const TAKT_RUNTIME_COMPANION_KEY = "companion";
52062
+ const TAKT_RUNTIME_ENABLED_KEY = "enabled";
52063
+ const TAKT_DEFAULT_COMPANION_ENABLED = false;
51692
52064
  const TAKT_SECURITY_POLICIES = {
51693
52065
  workflow_arpeggio: [
51694
52066
  "custom_data_source_modules",
@@ -51942,21 +52314,156 @@ function runtimeProfiles(runtime) {
51942
52314
  const profiles = provider[TAKT_RUNTIME_PROFILES_KEY];
51943
52315
  return isPlainObject$1(profiles) ? profiles : {};
51944
52316
  }
52317
+ /** A `targets` map (`personas`, `tags`, …) with at least one non-empty entry. */
52318
+ function hasTargetContent(targets) {
52319
+ return isPlainObject$1(targets) && Object.values(targets).some(hasEntries);
52320
+ }
52321
+ /** The profile names an assignment points at: `profile` plus every `ladder` rung. */
52322
+ function assignmentProfileNames(assignment) {
52323
+ if (!isPlainObject$1(assignment)) return [];
52324
+ const names = [];
52325
+ const profile = assignment[TAKT_RUNTIME_PROFILE_KEY];
52326
+ if (typeof profile === "string") names.push(profile);
52327
+ const ladder = assignment[TAKT_RUNTIME_LADDER_KEY];
52328
+ if (Array.isArray(ladder)) names.push(...ladder.filter((rung) => typeof rung === "string"));
52329
+ return names;
52330
+ }
52331
+ /** The assignments under a `targets` map, split by whether they route companions. */
52332
+ function targetAssignments(targets, { companions }) {
52333
+ if (!isPlainObject$1(targets)) return [];
52334
+ return Object.entries(targets).filter(([name]) => name === TAKT_RUNTIME_COMPANIONS_TARGET_KEY === companions).flatMap(([, targetMap]) => isPlainObject$1(targetMap) ? Object.values(targetMap) : []);
52335
+ }
52336
+ /**
52337
+ * Record the profiles a `defaults` + `targets` pair (the top-level provider
52338
+ * section or one named assignment set) reaches, keeping companion roots apart
52339
+ * from the roots agents use.
52340
+ */
52341
+ function addAssignmentSetRoots({ set, companionRoots, agentRoots }) {
52342
+ for (const name of assignmentProfileNames(set[TAKT_RUNTIME_DEFAULTS_KEY])) agentRoots.add(name);
52343
+ const targets = set[TAKT_RUNTIME_TARGETS_KEY];
52344
+ for (const assignment of targetAssignments(targets, { companions: false })) for (const name of assignmentProfileNames(assignment)) agentRoots.add(name);
52345
+ for (const assignment of targetAssignments(targets, { companions: true })) for (const name of assignmentProfileNames(assignment)) companionRoots.add(name);
52346
+ }
52347
+ /** The profiles `auto_routing` reaches: the router plus every pool candidate/fallback. */
52348
+ function addAutoRoutingRoots(autoRouting, roots) {
52349
+ if (!isPlainObject$1(autoRouting)) return;
52350
+ const routerProfile = autoRouting[TAKT_RUNTIME_ROUTER_PROFILE_KEY];
52351
+ if (typeof routerProfile === "string") roots.add(routerProfile);
52352
+ const pools = autoRouting[TAKT_RUNTIME_POOLS_KEY];
52353
+ if (!isPlainObject$1(pools)) return;
52354
+ for (const pool of Object.values(pools)) {
52355
+ if (!isPlainObject$1(pool)) continue;
52356
+ const candidates = pool[TAKT_RUNTIME_CANDIDATES_KEY];
52357
+ for (const candidate of Array.isArray(candidates) ? candidates : []) for (const name of assignmentProfileNames(candidate)) roots.add(name);
52358
+ const fallback = pool[TAKT_RUNTIME_FALLBACK_PROFILE_KEY];
52359
+ if (typeof fallback === "string") roots.add(fallback);
52360
+ }
52361
+ }
52362
+ /** Every profile reachable from `roots` by following `extends`. */
52363
+ function profileClosure(profiles, roots) {
52364
+ const closure = /* @__PURE__ */ new Set();
52365
+ const visit = (name) => {
52366
+ if (closure.has(name)) return;
52367
+ closure.add(name);
52368
+ const profile = profiles[name];
52369
+ const parent = isPlainObject$1(profile) ? profile[TAKT_RUNTIME_EXTENDS_KEY] : void 0;
52370
+ if (typeof parent === "string") visit(parent);
52371
+ };
52372
+ for (const root of roots) visit(root);
52373
+ return closure;
52374
+ }
52375
+ /** The `targets` map minus its `companions` entry (`undefined` when not a map). */
52376
+ function withoutCompanionTargets(targets) {
52377
+ if (!isPlainObject$1(targets)) return;
52378
+ const { [TAKT_RUNTIME_COMPANIONS_TARGET_KEY]: _companions, ...remaining } = targets;
52379
+ return remaining;
52380
+ }
52381
+ /**
52382
+ * The named assignment sets with their companion targets removed; a set left
52383
+ * with neither `defaults` nor a non-empty `targets` map is dropped, and
52384
+ * `undefined` means none survived.
52385
+ */
52386
+ function withoutCompanionAssignments(assignments) {
52387
+ if (!isPlainObject$1(assignments)) return;
52388
+ const remaining = {};
52389
+ for (const [name, set] of Object.entries(assignments)) {
52390
+ if (!isPlainObject$1(set)) continue;
52391
+ const defaults = set[TAKT_RUNTIME_DEFAULTS_KEY];
52392
+ const targets = withoutCompanionTargets(set[TAKT_RUNTIME_TARGETS_KEY]);
52393
+ const keepTargets = hasTargetContent(targets);
52394
+ if (defaults === void 0 && !keepTargets) continue;
52395
+ remaining[name] = {
52396
+ ...defaults === void 0 ? {} : { [TAKT_RUNTIME_DEFAULTS_KEY]: defaults },
52397
+ ...keepTargets ? { [TAKT_RUNTIME_TARGETS_KEY]: targets } : {}
52398
+ };
52399
+ }
52400
+ return Object.keys(remaining).length > 0 ? remaining : void 0;
52401
+ }
52402
+ /** Whether the section (or any of its named assignment sets) routes companions at all. */
52403
+ function hasCompanionTargets(section) {
52404
+ const routesCompanions = (set) => {
52405
+ const targets = isPlainObject$1(set) ? set[TAKT_RUNTIME_TARGETS_KEY] : void 0;
52406
+ return isPlainObject$1(targets) && targets[TAKT_RUNTIME_COMPANIONS_TARGET_KEY] !== void 0;
52407
+ };
52408
+ const assignments = section[TAKT_RUNTIME_ASSIGNMENTS_KEY];
52409
+ return routesCompanions(section) || isPlainObject$1(assignments) && Object.values(assignments).some(routesCompanions);
52410
+ }
52411
+ /**
52412
+ * The `provider:` section as Takt itself evaluates it, mirroring upstream's
52413
+ * `getEffectiveProviderSection`: unless `companion.enabled` is `true`
52414
+ * (companions are opt-in), the `targets.companions` maps — top-level and inside
52415
+ * every named assignment set — are dropped, along with each profile that only a
52416
+ * companion target reaches through `extends`. A profile an agent assignment
52417
+ * (`defaults`, the other `targets` maps, `auto_routing`) also reaches is kept.
52418
+ * https://github.com/nrslib/takt/blob/main/src/infra/config/runtime-provider/schema.ts
52419
+ */
52420
+ function effectiveRuntimeProviderSection(runtime) {
52421
+ const section = runtime[TAKT_RUNTIME_PROVIDER_KEY];
52422
+ if (!isPlainObject$1(section)) return;
52423
+ const companion = runtime[TAKT_RUNTIME_COMPANION_KEY];
52424
+ if (((isPlainObject$1(companion) ? companion[TAKT_RUNTIME_ENABLED_KEY] : void 0) ?? TAKT_DEFAULT_COMPANION_ENABLED) === true || !hasCompanionTargets(section)) return section;
52425
+ const profiles = runtimeProfiles(runtime);
52426
+ const companionRoots = /* @__PURE__ */ new Set();
52427
+ const agentRoots = /* @__PURE__ */ new Set();
52428
+ addAssignmentSetRoots({
52429
+ set: section,
52430
+ companionRoots,
52431
+ agentRoots
52432
+ });
52433
+ const assignments = section[TAKT_RUNTIME_ASSIGNMENTS_KEY];
52434
+ for (const set of isPlainObject$1(assignments) ? Object.values(assignments) : []) if (isPlainObject$1(set)) addAssignmentSetRoots({
52435
+ set,
52436
+ companionRoots,
52437
+ agentRoots
52438
+ });
52439
+ addAutoRoutingRoots(section[TAKT_RUNTIME_AUTO_ROUTING_KEY], agentRoots);
52440
+ const companionClosure = profileClosure(profiles, companionRoots);
52441
+ const agentClosure = profileClosure(profiles, agentRoots);
52442
+ const effective = {
52443
+ ...section,
52444
+ [TAKT_RUNTIME_PROFILES_KEY]: Object.fromEntries(Object.entries(profiles).filter(([name]) => !companionClosure.has(name) || agentClosure.has(name)))
52445
+ };
52446
+ for (const [key, value] of [[TAKT_RUNTIME_TARGETS_KEY, withoutCompanionTargets(section[TAKT_RUNTIME_TARGETS_KEY])], [TAKT_RUNTIME_ASSIGNMENTS_KEY, withoutCompanionAssignments(assignments)]]) if (value === void 0) delete effective[key];
52447
+ else effective[key] = value;
52448
+ return effective;
52449
+ }
51945
52450
  /**
51946
52451
  * Whether a parsed `runtime.yaml` puts Takt into runtime mode, mirroring
51947
- * upstream's mode detection: the `provider:` section must carry an actual
51948
- * assignment — a non-empty `defaults`, `profiles` or `auto_routing`, or a
51949
- * `targets` map with at least one non-empty nested map. The file existing is not
51950
- * enough, and empty nested maps (`defaults: {}`, `targets: { personas: {} }`)
51951
- * must not flip the mode.
52452
+ * upstream's mode detection: the effective `provider:` section (see
52453
+ * `effectiveRuntimeProviderSection` — companion-only routing does not count
52454
+ * while companions are disabled) must carry an actual assignment — a non-empty
52455
+ * `defaults`, `profiles` or `auto_routing`, a `targets` map with at least one
52456
+ * non-empty nested map, or a named assignment set with a non-empty `defaults`
52457
+ * or such a `targets` map. The file existing is not enough, and empty nested
52458
+ * maps (`defaults: {}`, `targets: { personas: {} }`) must not flip the mode.
51952
52459
  * https://github.com/nrslib/takt/blob/main/src/infra/config/runtime-provider/mode.ts
51953
52460
  */
51954
52461
  function isRuntimeModeActive(runtime) {
51955
- const provider = runtime[TAKT_RUNTIME_PROVIDER_KEY];
51956
- if (!isPlainObject$1(provider)) return false;
51957
- if (hasEntries(provider[TAKT_RUNTIME_DEFAULTS_KEY]) || hasEntries(provider[TAKT_RUNTIME_PROFILES_KEY]) || hasEntries(provider[TAKT_RUNTIME_AUTO_ROUTING_KEY])) return true;
51958
- const targets = provider[TAKT_RUNTIME_TARGETS_KEY];
51959
- return isPlainObject$1(targets) && Object.values(targets).some(hasEntries);
52462
+ const section = effectiveRuntimeProviderSection(runtime);
52463
+ if (section === void 0) return false;
52464
+ const assignments = section[TAKT_RUNTIME_ASSIGNMENTS_KEY];
52465
+ const hasAssignments = isPlainObject$1(assignments) && Object.values(assignments).some((set) => isPlainObject$1(set) && (hasEntries(set[TAKT_RUNTIME_DEFAULTS_KEY]) || hasTargetContent(set[TAKT_RUNTIME_TARGETS_KEY])));
52466
+ return hasEntries(section[TAKT_RUNTIME_DEFAULTS_KEY]) || hasEntries(section[TAKT_RUNTIME_PROFILES_KEY]) || hasEntries(section[TAKT_RUNTIME_AUTO_ROUTING_KEY]) || hasTargetContent(section[TAKT_RUNTIME_TARGETS_KEY]) || hasAssignments;
51960
52467
  }
51961
52468
  /** The provider named by the runtime profile Takt would use by default. */
51962
52469
  function resolveRuntimeProvider(runtime) {
@@ -51979,10 +52486,13 @@ function resolveRuntimeProvider(runtime) {
51979
52486
  * shape the `takt` override uses. Each profile's options belong to that
51980
52487
  * profile's own provider; when several profiles name the same provider they are
51981
52488
  * merged in document order, so a later profile wins on a colliding option key.
52489
+ * Profiles Takt itself ignores (companion-only ones while companions are
52490
+ * disabled) are skipped, as they never reach an agent.
51982
52491
  */
51983
52492
  function collectRuntimeProviderOptions(runtime) {
51984
52493
  const collected = {};
51985
- for (const profile of Object.values(runtimeProfiles(runtime))) {
52494
+ const profiles = effectiveRuntimeProviderSection(runtime)?.[TAKT_RUNTIME_PROFILES_KEY];
52495
+ for (const profile of Object.values(isPlainObject$1(profiles) ? profiles : {})) {
51986
52496
  if (!isPlainObject$1(profile)) continue;
51987
52497
  const providerName = profile[TAKT_RUNTIME_PROVIDER_KEY];
51988
52498
  const options = profile[TAKT_RUNTIME_OPTIONS_KEY];
@@ -52021,13 +52531,20 @@ async function readTaktRuntimeFile({ filePath, logger }) {
52021
52531
  };
52022
52532
  }
52023
52533
  }
52534
+ /** A map-valued key of a `provider:` section, or `undefined` when it is not a map. */
52535
+ function runtimeSectionMap(section, key) {
52536
+ return isPlainObject$1(section[key]) ? section[key] : void 0;
52537
+ }
52024
52538
  /**
52025
52539
  * Collapse the project and global `runtime.yaml` into the single document Takt
52026
- * itself resolves against, matching upstream's loader: `profiles` is a union
52027
- * with the project definition of a same-named profile replacing the global one,
52028
- * while `defaults`, `targets` and `auto_routing` are taken from the project file
52029
- * whole whenever it states them at all (`??`, so a project `targets: {}` masks
52030
- * the global one rather than merging with it).
52540
+ * itself resolves against, matching upstream's loader: `profiles` and
52541
+ * `assignments` are unions with the project definition of a same-named entry
52542
+ * replacing the global one, while `defaults`, `targets` and `auto_routing` are
52543
+ * taken from the project file whole whenever it states them at all (`??`, so a
52544
+ * project `targets: {}` masks the global one rather than merging with it). The
52545
+ * top-level `companion.enabled` — which decides whether companion routing
52546
+ * counts for mode detection — is carried as upstream carries it: unset when
52547
+ * neither file states it, otherwise on only when neither file turns it off.
52031
52548
  * https://github.com/nrslib/takt/blob/main/src/infra/config/runtime-provider/loader.ts
52032
52549
  *
52033
52550
  * Merging before mode detection matters in both directions: a project file
@@ -52043,14 +52560,15 @@ function mergeTaktRuntimeConfigs({ project, global: globalConfig }) {
52043
52560
  const projectSection = sectionOf(project);
52044
52561
  const globalSection = sectionOf(globalConfig);
52045
52562
  const replaced = (key) => projectSection[key] ?? globalSection[key];
52046
- const profilesOf = (section) => isPlainObject$1(section[TAKT_RUNTIME_PROFILES_KEY]) ? section[TAKT_RUNTIME_PROFILES_KEY] : void 0;
52047
- const globalProfiles = profilesOf(globalSection);
52048
- const projectProfiles = profilesOf(projectSection);
52049
52563
  const provider = {};
52050
- if (globalProfiles !== void 0 || projectProfiles !== void 0) provider[TAKT_RUNTIME_PROFILES_KEY] = {
52051
- ...globalProfiles,
52052
- ...projectProfiles
52053
- };
52564
+ for (const key of [TAKT_RUNTIME_PROFILES_KEY, TAKT_RUNTIME_ASSIGNMENTS_KEY]) {
52565
+ const globalMap = runtimeSectionMap(globalSection, key);
52566
+ const projectMap = runtimeSectionMap(projectSection, key);
52567
+ if (globalMap !== void 0 || projectMap !== void 0) provider[key] = {
52568
+ ...globalMap,
52569
+ ...projectMap
52570
+ };
52571
+ }
52054
52572
  for (const key of [
52055
52573
  TAKT_RUNTIME_DEFAULTS_KEY,
52056
52574
  TAKT_RUNTIME_TARGETS_KEY,
@@ -52059,7 +52577,17 @@ function mergeTaktRuntimeConfigs({ project, global: globalConfig }) {
52059
52577
  const value = replaced(key);
52060
52578
  if (value !== void 0) provider[key] = value;
52061
52579
  }
52062
- return { [TAKT_RUNTIME_PROVIDER_KEY]: provider };
52580
+ const enabledOf = (config) => {
52581
+ const companion = config?.[TAKT_RUNTIME_COMPANION_KEY];
52582
+ return isPlainObject$1(companion) ? companion[TAKT_RUNTIME_ENABLED_KEY] : void 0;
52583
+ };
52584
+ const globalEnabled = enabledOf(globalConfig);
52585
+ const projectEnabled = enabledOf(project);
52586
+ const companion = globalEnabled === void 0 && projectEnabled === void 0 ? void 0 : { [TAKT_RUNTIME_ENABLED_KEY]: globalEnabled !== false && projectEnabled !== false };
52587
+ return {
52588
+ [TAKT_RUNTIME_PROVIDER_KEY]: provider,
52589
+ ...companion === void 0 ? {} : { [TAKT_RUNTIME_COMPANION_KEY]: companion }
52590
+ };
52063
52591
  }
52064
52592
  /**
52065
52593
  * The runtime provider configuration in force for a generate run: the project
@@ -59728,7 +60256,8 @@ const GrokcliSkillFrontmatterSchema = z.looseObject({
59728
60256
  name: z.string(),
59729
60257
  description: z.string(),
59730
60258
  "user-invocable": z.optional(z.boolean()),
59731
- "disable-model-invocation": z.optional(z.boolean())
60259
+ "disable-model-invocation": z.optional(z.boolean()),
60260
+ paths: z.optional(z.union([z.string(), z.array(z.string())]))
59732
60261
  });
59733
60262
  /**
59734
60263
  * Represents a Grok Build skill directory.
@@ -59787,7 +60316,8 @@ var GrokcliSkill = class GrokcliSkill extends ToolSkill {
59787
60316
  const frontmatter = this.getFrontmatter();
59788
60317
  const grokcliSection = {
59789
60318
  ...frontmatter["user-invocable"] !== void 0 && { "user-invocable": frontmatter["user-invocable"] },
59790
- ...frontmatter["disable-model-invocation"] !== void 0 && { "disable-model-invocation": frontmatter["disable-model-invocation"] }
60319
+ ...frontmatter["disable-model-invocation"] !== void 0 && { "disable-model-invocation": frontmatter["disable-model-invocation"] },
60320
+ ...frontmatter.paths !== void 0 && { paths: frontmatter.paths }
59791
60321
  };
59792
60322
  const rulesyncFrontmatter = {
59793
60323
  name: frontmatter.name,
@@ -59822,7 +60352,8 @@ var GrokcliSkill = class GrokcliSkill extends ToolSkill {
59822
60352
  name: rulesyncFrontmatter.name,
59823
60353
  description: rulesyncFrontmatter.description,
59824
60354
  ...resolvedUserInvocable !== void 0 && { "user-invocable": resolvedUserInvocable },
59825
- ...resolvedDisableModelInvocation !== void 0 && { "disable-model-invocation": resolvedDisableModelInvocation }
60355
+ ...resolvedDisableModelInvocation !== void 0 && { "disable-model-invocation": resolvedDisableModelInvocation },
60356
+ ...grokcliSection?.paths !== void 0 && { paths: grokcliSection.paths }
59826
60357
  };
59827
60358
  return new GrokcliSkill({
59828
60359
  outputRoot,
@@ -62473,7 +63004,10 @@ var WarpSkill = class WarpSkill extends ToolSkill {
62473
63004
  }
62474
63005
  }
62475
63006
  static getSettablePaths({ global: _global = false } = {}) {
62476
- return { relativeDirPath: WARP_SKILLS_DIR_PATH };
63007
+ return {
63008
+ relativeDirPath: WARP_SKILLS_DIR_PATH,
63009
+ importOnlySkillRoots: [AGENTSMD_SKILLS_DIR_PATH]
63010
+ };
62477
63011
  }
62478
63012
  getFrontmatter() {
62479
63013
  return WarpSkillFrontmatterSchema.parse(this.requireMainFileFrontmatter());
@@ -62537,12 +63071,14 @@ var WarpSkill = class WarpSkill extends ToolSkill {
62537
63071
  return targets.includes("*") || targets.includes("warp");
62538
63072
  }
62539
63073
  /**
62540
- * Commands are emitted into this same skills tree as `<slug>/SKILL.md`
62541
- * (see `WarpCommand`), so a directory matching a current rulesync command
62542
- * slug is owned by the commands feature: it must not be imported as a
62543
- * skill nor deleted as an orphan skill.
63074
+ * Commands are emitted into the `.warp/skills/` tree as `<slug>/SKILL.md`
63075
+ * (see `WarpCommand`), so a directory there matching a current rulesync
63076
+ * command slug is owned by the commands feature: it must not be imported as
63077
+ * a skill nor deleted as an orphan skill. The import-only `.agents/skills/`
63078
+ * root never receives commands, so a same-named skill in it stays a skill.
62544
63079
  */
62545
- static async isDirOwned({ dirName, inputRoots }) {
63080
+ static async isDirOwned({ relativeDirPath, dirName, inputRoots }) {
63081
+ if (relativeDirPath !== WARP_SKILLS_DIR_PATH) return true;
62546
63082
  return !await rulesyncCommandSlugExists({
62547
63083
  inputRoots,
62548
63084
  dirName
@@ -62755,6 +63291,25 @@ const ZedSkillFrontmatterSchema = z.looseObject({
62755
63291
  description: z.string(),
62756
63292
  "disable-model-invocation": z.optional(z.boolean())
62757
63293
  });
63294
+ const ZED = "Zed";
63295
+ const ZED_LOAD_FAILURE = "does not load the skill otherwise";
63296
+ /**
63297
+ * Collect the rules Zed enforces on a skill's frontmatter that the loose
63298
+ * schema above does not — `name` rules first, then `description`. Returned as
63299
+ * warnings rather than thrown: the canonical skill is shared with every other
63300
+ * target, so a name Zed rejects must not stop the generate run for the rest.
63301
+ */
63302
+ function collectZedSkillViolations({ name, description }) {
63303
+ const violations = [];
63304
+ if (name.length === 0) violations.push("`name` must not be empty; Zed does not load a skill without one");
63305
+ else violations.push(...collectSkillNameViolations({
63306
+ name,
63307
+ authority: ZED,
63308
+ consequence: ZED_LOAD_FAILURE
63309
+ }));
63310
+ if (description.length > 1024) violations.push(`\`description\` is ${description.length} characters; ${ZED} loads the skill but warns past ${SKILL_DESCRIPTION_MAX_LENGTH}`);
63311
+ return violations;
63312
+ }
62758
63313
  /**
62759
63314
  * Represents a Zed agent skill directory.
62760
63315
  * Skills are stored under .agents/skills/ (project) or ~/.agents/skills/ (global)
@@ -62823,9 +63378,10 @@ var ZedSkill = class ZedSkill extends ToolSkill {
62823
63378
  global: this.global
62824
63379
  });
62825
63380
  }
62826
- static fromRulesyncSkill({ outputRoot = process.cwd(), rulesyncSkill, validate = true, global = false }) {
63381
+ static fromRulesyncSkill({ outputRoot = process.cwd(), rulesyncSkill, validate = true, global = false, logger }) {
62827
63382
  const settablePaths = ZedSkill.getSettablePaths({ global });
62828
63383
  const rulesyncFrontmatter = rulesyncSkill.getFrontmatter();
63384
+ const dirName = rulesyncSkill.getDirName();
62829
63385
  const zedSection = rulesyncFrontmatter.zed;
62830
63386
  const resolvedDisableModelInvocation = resolveDisableModelInvocation({
62831
63387
  rootFrontmatter: rulesyncFrontmatter,
@@ -62837,10 +63393,17 @@ var ZedSkill = class ZedSkill extends ToolSkill {
62837
63393
  ...zedSection,
62838
63394
  ...resolvedDisableModelInvocation !== void 0 && { "disable-model-invocation": resolvedDisableModelInvocation }
62839
63395
  };
63396
+ ZedSkill.reportSkillViolations({
63397
+ outputRoot,
63398
+ relativeDirPath: settablePaths.relativeDirPath,
63399
+ dirName,
63400
+ frontmatter: zedFrontmatter,
63401
+ logger
63402
+ });
62840
63403
  return new ZedSkill({
62841
63404
  outputRoot,
62842
63405
  relativeDirPath: settablePaths.relativeDirPath,
62843
- dirName: rulesyncSkill.getDirName(),
63406
+ dirName,
62844
63407
  frontmatter: zedFrontmatter,
62845
63408
  body: rulesyncSkill.getBody(),
62846
63409
  otherFiles: rulesyncSkill.getOtherFiles(),
@@ -62848,6 +63411,18 @@ var ZedSkill = class ZedSkill extends ToolSkill {
62848
63411
  global
62849
63412
  });
62850
63413
  }
63414
+ /**
63415
+ * Warn about every rule Zed enforces on the skill about to be written. The
63416
+ * reported path includes `outputRoot` so a global-scope skill points at the
63417
+ * file under the home directory rather than a same-named project path.
63418
+ */
63419
+ static reportSkillViolations({ outputRoot, relativeDirPath, dirName, frontmatter, logger }) {
63420
+ warnSkillViolations({
63421
+ skillPath: join(outputRoot, relativeDirPath, dirName, SKILL_FILE_NAME),
63422
+ violations: collectZedSkillViolations(frontmatter),
63423
+ logger
63424
+ });
63425
+ }
62851
63426
  static isTargetedByRulesyncSkill(rulesyncSkill) {
62852
63427
  const targets = rulesyncSkill.getFrontmatter().targets;
62853
63428
  return targets.includes("*") || targets.includes("zed");
@@ -79705,4 +80280,4 @@ async function importChecksCore(params) {
79705
80280
  //#endregion
79706
80281
  export { RulesyncCommand as $, toPosixPath as $t, CLAUDECODE_MEMORIES_DIR_NAME as A, RULESYNC_XDG_CONFIG_HOME_DEFAULT_DIR_NAME as An, checkPathTraversal as At, RulesyncSubagent as B, stripControlCharacters as Bn, listDirectoryEntryNames as Bt, COMMANDCODE_DIR as C, RULESYNC_PERMISSIONS_SCHEMA_URL as Cn, withWarnOnceScope as Ct, CODEXCLI_DIR as D, RULESYNC_SOURCES_LOCK_RELATIVE_FILE_PATH as Dn, assertDirectoryIfExists as Dt, CODEXCLI_BASH_RULES_FILE_NAME as E, RULESYNC_SKILLS_RELATIVE_DIR_PATH as En, applyFileMode as Et, caseFoldIdentity as F, formatError as Fn, getFileSize as Ft, RulesyncRuleFrontmatterSchema as G, readFileContentOrNull as Gt, RulesyncSkill as H, stripHiddenCharacters as Hn, listSubdirectoryNames as Ht, groupSpellingsByCaseFoldedIdentity as I, truncateText as In, getHomeDirectory as It, RulesyncIgnore as J, removeFile as Jt, RulesyncPermissions as K, removeDirectory as Kt, AUGMENTCODE_DIR as L, hasDeceptiveHiddenCharacters as Ln, isFileNotFoundError as Lt, CLAUDECODE_SKILLS_DIR_PATH as M, ALL_FEATURES as Mn, directoryExists as Mt, FACTORYDROID_DIR as N, ALL_FEATURES_WITH_WILDCARD as Nn, ensureDir as Nt, CLAUDECODE_DIR as O, RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH as On, assertTreeContainsNoSymlinks as Ot, FACTORYDROID_SETTINGS_LOCAL_FILE_NAME as P, DEPRECATED_FEATURE_REPLACEMENTS as Pn, fileExists as Pt, parseJsonc as Q, runWithDirectoryRollback as Qt, AUGMENTCODE_SETTINGS_LOCAL_FILE_NAME as R, hasEnclosingMarkOutsideKeycap as Rn, isFileSystemError as Rt, QWENCODE_LOCAL_RULE_FILE_NAME as S, RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH as Sn, resetRunWarningState as St, ChecksProcessor as T, RULESYNC_RULES_RELATIVE_DIR_PATH as Tn, ErrorCodes as Tt, RulesyncSkillFrontmatterSchema as U, pathEscapesRoot as Ut, RulesyncSubagentFrontmatterSchema as V, stripControlCharactersKeepingLineFeeds as Vn, listFilePathsRecursively as Vt, RulesyncRule as W, readFileContent as Wt, getRulesyncSourceCandidates as X, removeTempDirectory as Xt, RulesyncHooks as Y, removeFileStrict as Yt, resolveRulesyncSourceWritePath as Z, resolvePath as Zt, IgnoreProcessor as _, RULESYNC_IGNORE_RELATIVE_FILE_PATH as _n, JsonLogger as _t, getProcessorRegistryEntry as a, ToolTargetSchema as an, SHARED_USER_MANAGED_CONFIG_PATHS as at, CommandsProcessor as b, RULESYNC_MCP_SCHEMA_URL as bn, warnOnConflictingFlags as bt, RulesProcessor as c, MAX_FILE_SIZE as cn, mergeInputRootConfigs as ct, CODEBUDDY_DIR as d, RULESYNC_COMMANDS_RELATIVE_DIR_PATH as dn, ConfigFileSchema as dt, writeFileBuffer as en, RulesyncCommandFrontmatterSchema as et, CODEBUDDY_LOCAL_RULE_FILE_NAME as f, RULESYNC_CONFIG_RELATIVE_FILE_PATH as fn, GITIGNORE_DESTINATION_KEY as ft, McpProcessor as g, RULESYNC_HOOKS_RELATIVE_FILE_PATH as gn, ConsoleLogger as gt, shortenToWidth as h, RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH as hn, findControlCharacter as ht, inspectInputRoots as i, PACKAGING_TOOL_TARGETS as in, loadYaml as it, CLAUDECODE_SETTINGS_LOCAL_FILE_NAME as j, parseCommaSeparatedList as jn, createTempDirectory as jt, CLAUDECODE_LOCAL_RULE_FILE_NAME as k, RULESYNC_USER_CONFIG_DIR_NAME as kn, assertWritablePathInsideRoot as kt, SubagentsProcessor as l, RULESYNC_AIIGNORE_RELATIVE_FILE_PATH as ln, resolveEffectiveInputRoots as lt, displayWidthOf as m, RULESYNC_CURATED_RULES_RELATIVE_DIR_PATH as mn, assertTargetsFeaturesExclusive as mt, formatSourceLoadFailure as n, ALL_TOOL_TARGETS as nn, RulesyncCheckFrontmatterSchema as nt, convertFromTool as o, CURATED_RULES_FEATURE_SUBDIR as on, SKILL_FILE_NAME as ot, ELLIPSIS_WIDTH as p, RULESYNC_CONFIG_SCHEMA_URL as pn, SourceEntrySchema as pt, RulesyncMcp as q, removeDirectoryStrict as qt, generate as r, ALL_TOOL_TARGETS_WITH_WILDCARD as rn, stringifyFrontmatter as rt, isPackagingToolTarget as s, FEATURE_SOURCE_TREE_ENTRIES as sn, ConfigResolver as st, importFromTool as t, writeFileContent as tn, RulesyncCheck as tt, SkillsProcessor as u, RULESYNC_CHECKS_RELATIVE_DIR_PATH as un, CONFLICTING_TARGET_PAIRS as ut, HooksProcessor as v, RULESYNC_LOCAL_CONFIG_RELATIVE_FILE_PATH as vn, WarningCollectingLogger as vt, COMMANDCODE_SETTINGS_LOCAL_FILE_NAME as w, RULESYNC_RELATIVE_DIR_PATH as wn, CLIError as wt, QWENCODE_DIR as x, RULESYNC_NPM_SOURCES_LOCK_RELATIVE_FILE_PATH as xn, withFallbackLoggerTarget as xt, CRUSH_LOCAL_RULE_FILE_NAME as y, RULESYNC_MCP_RELATIVE_FILE_PATH as yn, fallbackLogger as yt, getLocalSkillDirNames as z, quoteForLog as zn, isSymlink as zt };
79707
80282
 
79708
- //# sourceMappingURL=import-DxRza3o_.js.map
80283
+ //# sourceMappingURL=import-D_iukHSy.js.map