rulesync 16.30.2 → 16.31.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.
- package/README.md +3 -0
- package/dist/cli/index.cjs +74 -21
- package/dist/cli/index.js +74 -21
- package/dist/cli/index.js.map +1 -1
- package/dist/{import-auwKp4Mm.cjs → import-Cwe8RKJs.cjs} +3850 -283
- package/dist/{import-BknrMODR.js → import-D2GhBXJV.js} +3817 -268
- package/dist/import-D2GhBXJV.js.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +13 -1
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +13 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/import-BknrMODR.js.map +0 -1
|
@@ -422,6 +422,8 @@ const parseCommaSeparatedList = (value) => value.split(",").map((s) => s.trim())
|
|
|
422
422
|
const { join: join$1 } = posix;
|
|
423
423
|
const RULESYNC_CONFIG_RELATIVE_FILE_PATH = "rulesync.jsonc";
|
|
424
424
|
const RULESYNC_LOCAL_CONFIG_RELATIVE_FILE_PATH = "rulesync.local.jsonc";
|
|
425
|
+
const RULESYNC_USER_CONFIG_DIR_NAME = "rulesync";
|
|
426
|
+
const RULESYNC_XDG_CONFIG_HOME_DEFAULT_DIR_NAME = ".config";
|
|
425
427
|
const RULESYNC_RELATIVE_DIR_PATH = ".rulesync";
|
|
426
428
|
const RULES_FEATURE_SUBDIR = "rules";
|
|
427
429
|
const CURATED_RULES_FEATURE_SUBDIR = join$1(RULES_FEATURE_SUBDIR, ".curated");
|
|
@@ -470,6 +472,7 @@ const rulesProcessorToolTargetTuple = [
|
|
|
470
472
|
"antigravity-plugin",
|
|
471
473
|
"augmentcode",
|
|
472
474
|
"augmentcode-legacy",
|
|
475
|
+
"bob",
|
|
473
476
|
"claudecode",
|
|
474
477
|
"claudecode-legacy",
|
|
475
478
|
"cline",
|
|
@@ -477,6 +480,7 @@ const rulesProcessorToolTargetTuple = [
|
|
|
477
480
|
"codexcli",
|
|
478
481
|
"copilot",
|
|
479
482
|
"copilotcli",
|
|
483
|
+
"cortexcode",
|
|
480
484
|
"crush",
|
|
481
485
|
"cursor",
|
|
482
486
|
"deepagents",
|
|
@@ -498,6 +502,7 @@ const rulesProcessorToolTargetTuple = [
|
|
|
498
502
|
"replit",
|
|
499
503
|
"roo",
|
|
500
504
|
"rovodev",
|
|
505
|
+
"tabnine",
|
|
501
506
|
"takt",
|
|
502
507
|
"vibe",
|
|
503
508
|
"warp",
|
|
@@ -512,6 +517,7 @@ const ignoreProcessorToolTargetTuple = [
|
|
|
512
517
|
"aiassistant",
|
|
513
518
|
"antigravity-cli",
|
|
514
519
|
"augmentcode",
|
|
520
|
+
"bob",
|
|
515
521
|
"claudecode",
|
|
516
522
|
"claudecode-legacy",
|
|
517
523
|
"cline",
|
|
@@ -526,6 +532,7 @@ const ignoreProcessorToolTargetTuple = [
|
|
|
526
532
|
"qwencode",
|
|
527
533
|
"reasonix",
|
|
528
534
|
"roo",
|
|
535
|
+
"tabnine",
|
|
529
536
|
"devin",
|
|
530
537
|
"vibe",
|
|
531
538
|
"warp",
|
|
@@ -539,6 +546,7 @@ const mcpProcessorToolTargetTuple = [
|
|
|
539
546
|
"antigravity-ide",
|
|
540
547
|
"antigravity-plugin",
|
|
541
548
|
"augmentcode",
|
|
549
|
+
"bob",
|
|
542
550
|
"claudecode",
|
|
543
551
|
"claudecode-plugin",
|
|
544
552
|
"claudecode-legacy",
|
|
@@ -546,6 +554,7 @@ const mcpProcessorToolTargetTuple = [
|
|
|
546
554
|
"codexcli",
|
|
547
555
|
"copilot",
|
|
548
556
|
"copilotcli",
|
|
557
|
+
"cortexcode",
|
|
549
558
|
"cursor",
|
|
550
559
|
"deepagents",
|
|
551
560
|
"factorydroid",
|
|
@@ -564,6 +573,7 @@ const mcpProcessorToolTargetTuple = [
|
|
|
564
573
|
"reasonix",
|
|
565
574
|
"roo",
|
|
566
575
|
"rovodev",
|
|
576
|
+
"tabnine",
|
|
567
577
|
"takt",
|
|
568
578
|
"vibe",
|
|
569
579
|
"warp",
|
|
@@ -577,6 +587,7 @@ const commandsProcessorToolTargetTuple = [
|
|
|
577
587
|
"antigravity-cli",
|
|
578
588
|
"antigravity-ide",
|
|
579
589
|
"augmentcode",
|
|
590
|
+
"bob",
|
|
580
591
|
"claudecode",
|
|
581
592
|
"claudecode-plugin",
|
|
582
593
|
"claudecode-legacy",
|
|
@@ -600,6 +611,7 @@ const commandsProcessorToolTargetTuple = [
|
|
|
600
611
|
"roo",
|
|
601
612
|
"zoocode",
|
|
602
613
|
"rovodev",
|
|
614
|
+
"tabnine",
|
|
603
615
|
"takt",
|
|
604
616
|
"devin",
|
|
605
617
|
"warp",
|
|
@@ -619,6 +631,7 @@ const subagentsProcessorToolTargetTuple = [
|
|
|
619
631
|
"codexcli",
|
|
620
632
|
"copilot",
|
|
621
633
|
"copilotcli",
|
|
634
|
+
"cortexcode",
|
|
622
635
|
"cursor",
|
|
623
636
|
"deepagents",
|
|
624
637
|
"devin",
|
|
@@ -637,6 +650,7 @@ const subagentsProcessorToolTargetTuple = [
|
|
|
637
650
|
"roo",
|
|
638
651
|
"zoocode",
|
|
639
652
|
"rovodev",
|
|
653
|
+
"tabnine",
|
|
640
654
|
"takt",
|
|
641
655
|
"vibe",
|
|
642
656
|
"zcode"
|
|
@@ -650,6 +664,7 @@ const skillsProcessorToolTargetTuple = [
|
|
|
650
664
|
"antigravity-ide",
|
|
651
665
|
"antigravity-plugin",
|
|
652
666
|
"augmentcode",
|
|
667
|
+
"bob",
|
|
653
668
|
"claudecode",
|
|
654
669
|
"claudecode-plugin",
|
|
655
670
|
"claudecode-legacy",
|
|
@@ -657,6 +672,7 @@ const skillsProcessorToolTargetTuple = [
|
|
|
657
672
|
"codexcli",
|
|
658
673
|
"copilot",
|
|
659
674
|
"copilotcli",
|
|
675
|
+
"cortexcode",
|
|
660
676
|
"crush",
|
|
661
677
|
"cursor",
|
|
662
678
|
"deepagents",
|
|
@@ -678,6 +694,7 @@ const skillsProcessorToolTargetTuple = [
|
|
|
678
694
|
"replit",
|
|
679
695
|
"roo",
|
|
680
696
|
"rovodev",
|
|
697
|
+
"tabnine",
|
|
681
698
|
"takt",
|
|
682
699
|
"vibe",
|
|
683
700
|
"warp",
|
|
@@ -700,6 +717,7 @@ const hooksProcessorToolTargetTuple = [
|
|
|
700
717
|
"codexcli",
|
|
701
718
|
"copilot",
|
|
702
719
|
"copilotcli",
|
|
720
|
+
"cortexcode",
|
|
703
721
|
"opencode",
|
|
704
722
|
"pi",
|
|
705
723
|
"factorydroid",
|
|
@@ -712,10 +730,12 @@ const hooksProcessorToolTargetTuple = [
|
|
|
712
730
|
"kiro-ide",
|
|
713
731
|
"devin",
|
|
714
732
|
"augmentcode",
|
|
733
|
+
"bob",
|
|
715
734
|
"junie",
|
|
716
735
|
"vibe",
|
|
717
736
|
"qwencode",
|
|
718
737
|
"reasonix",
|
|
738
|
+
"tabnine",
|
|
719
739
|
"grokcli",
|
|
720
740
|
"zcode"
|
|
721
741
|
];
|
|
@@ -748,6 +768,7 @@ const permissionsProcessorToolTargetTuple = [
|
|
|
748
768
|
"reasonix",
|
|
749
769
|
"roo",
|
|
750
770
|
"rovodev",
|
|
771
|
+
"tabnine",
|
|
751
772
|
"takt",
|
|
752
773
|
"vibe",
|
|
753
774
|
"warp",
|
|
@@ -3233,16 +3254,21 @@ const SHARED_USER_MANAGED_CONFIG_PATHS = [
|
|
|
3233
3254
|
".amp/settings.json",
|
|
3234
3255
|
".amp/settings.jsonc",
|
|
3235
3256
|
".antigravity/settings.json",
|
|
3257
|
+
".bob/settings.json",
|
|
3258
|
+
".bob/settings/settings.json",
|
|
3236
3259
|
".claude/settings.json",
|
|
3237
3260
|
".claude/settings.local.json",
|
|
3238
3261
|
".codex/config.toml",
|
|
3239
3262
|
".copilot/settings.json",
|
|
3240
3263
|
".github/copilot/settings.json",
|
|
3264
|
+
".cortex/settings.json",
|
|
3265
|
+
".snowflake/cortex/hooks.json",
|
|
3241
3266
|
".devin/config.json",
|
|
3242
3267
|
".factory/settings.json",
|
|
3243
3268
|
".grok/config.toml",
|
|
3244
3269
|
".rovodev/config.yml",
|
|
3245
3270
|
".rovodev/mcp.json",
|
|
3271
|
+
".tabnine/agent/settings.json",
|
|
3246
3272
|
".vibe/config.toml",
|
|
3247
3273
|
".zcode/config.json",
|
|
3248
3274
|
".zcode/cli/config.json",
|
|
@@ -4753,6 +4779,110 @@ const CANONICAL_TO_ZCODE_EVENT_NAMES = {
|
|
|
4753
4779
|
};
|
|
4754
4780
|
const ZCODE_TO_CANONICAL_EVENT_NAMES = Object.fromEntries(Object.entries(CANONICAL_TO_ZCODE_EVENT_NAMES).map(([k, v]) => [v, k]));
|
|
4755
4781
|
/**
|
|
4782
|
+
* Hook events supported by IBM Bob's lifecycle hooks.
|
|
4783
|
+
*
|
|
4784
|
+
* Bob reads `hooks` from `.bob/settings.json` (project) and
|
|
4785
|
+
* `~/.bob/settings/settings.json` (user) in the Claude-Code shape and fires
|
|
4786
|
+
* five events: SessionStart, UserPromptSubmit, PreToolUse, PostToolUse and
|
|
4787
|
+
* Stop. Only `command` hooks exist, and `matcher` (a regex over the tool
|
|
4788
|
+
* name) applies to the two tool events.
|
|
4789
|
+
*
|
|
4790
|
+
* @see https://bob.ibm.com/docs/ide/configuration/lifecycle-hooks
|
|
4791
|
+
*/
|
|
4792
|
+
const BOB_HOOK_EVENTS = [
|
|
4793
|
+
"sessionStart",
|
|
4794
|
+
"beforeSubmitPrompt",
|
|
4795
|
+
"preToolUse",
|
|
4796
|
+
"postToolUse",
|
|
4797
|
+
"stop"
|
|
4798
|
+
];
|
|
4799
|
+
const CANONICAL_TO_BOB_EVENT_NAMES = {
|
|
4800
|
+
sessionStart: "SessionStart",
|
|
4801
|
+
beforeSubmitPrompt: "UserPromptSubmit",
|
|
4802
|
+
preToolUse: "PreToolUse",
|
|
4803
|
+
postToolUse: "PostToolUse",
|
|
4804
|
+
stop: "Stop"
|
|
4805
|
+
};
|
|
4806
|
+
const BOB_TO_CANONICAL_EVENT_NAMES = Object.fromEntries(Object.entries(CANONICAL_TO_BOB_EVENT_NAMES).map(([k, v]) => [v, k]));
|
|
4807
|
+
/**
|
|
4808
|
+
* Hook events supported by Tabnine CLI.
|
|
4809
|
+
*
|
|
4810
|
+
* Tabnine CLI reads `hooks` from `.tabnine/agent/settings.json` (project) and
|
|
4811
|
+
* `~/.tabnine/agent/settings.json` (user) and fires eleven events with
|
|
4812
|
+
* PascalCase names. Only `command` hooks exist; `matcher` is a regex over the
|
|
4813
|
+
* tool name for the two tool events and an exact string for the lifecycle
|
|
4814
|
+
* events, and `timeout` is expressed in milliseconds.
|
|
4815
|
+
*
|
|
4816
|
+
* @see https://docs.tabnine.com/main/getting-started/tabnine-cli/features/hooks
|
|
4817
|
+
* @see https://docs.tabnine.com/main/getting-started/tabnine-cli/features/hooks/configuration
|
|
4818
|
+
*/
|
|
4819
|
+
const TABNINE_HOOK_EVENTS = [
|
|
4820
|
+
"sessionStart",
|
|
4821
|
+
"sessionEnd",
|
|
4822
|
+
"beforeSubmitPrompt",
|
|
4823
|
+
"stop",
|
|
4824
|
+
"preModelInvocation",
|
|
4825
|
+
"postModelInvocation",
|
|
4826
|
+
"beforeToolSelection",
|
|
4827
|
+
"preToolUse",
|
|
4828
|
+
"postToolUse",
|
|
4829
|
+
"preCompact",
|
|
4830
|
+
"notification"
|
|
4831
|
+
];
|
|
4832
|
+
const CANONICAL_TO_TABNINE_EVENT_NAMES = {
|
|
4833
|
+
sessionStart: "SessionStart",
|
|
4834
|
+
sessionEnd: "SessionEnd",
|
|
4835
|
+
beforeSubmitPrompt: "BeforeAgent",
|
|
4836
|
+
stop: "AfterAgent",
|
|
4837
|
+
preModelInvocation: "BeforeModel",
|
|
4838
|
+
postModelInvocation: "AfterModel",
|
|
4839
|
+
beforeToolSelection: "BeforeToolSelection",
|
|
4840
|
+
preToolUse: "BeforeTool",
|
|
4841
|
+
postToolUse: "AfterTool",
|
|
4842
|
+
preCompact: "PreCompress",
|
|
4843
|
+
notification: "Notification"
|
|
4844
|
+
};
|
|
4845
|
+
const TABNINE_TO_CANONICAL_EVENT_NAMES = Object.fromEntries(Object.entries(CANONICAL_TO_TABNINE_EVENT_NAMES).map(([k, v]) => [v, k]));
|
|
4846
|
+
/**
|
|
4847
|
+
* Hook events supported by Snowflake Cortex Code.
|
|
4848
|
+
*
|
|
4849
|
+
* Cortex Code reads `hooks` from `.cortex/settings.json` (project) and
|
|
4850
|
+
* `~/.snowflake/cortex/hooks.json` (user) in the Claude-Code-shaped layout:
|
|
4851
|
+
* PascalCase event names, a regex `matcher` over the tool name, and `command`
|
|
4852
|
+
* or `prompt` hooks with a `timeout` in seconds and an optional `enabled`
|
|
4853
|
+
* flag. `$CORTEX_PROJECT_DIR` resolves to the project root inside commands.
|
|
4854
|
+
*
|
|
4855
|
+
* @see https://docs.snowflake.com/en/user-guide/cortex-code/extensibility
|
|
4856
|
+
* @see https://docs.snowflake.com/en/user-guide/cortex-code/settings
|
|
4857
|
+
*/
|
|
4858
|
+
const CORTEXCODE_HOOK_EVENTS = [
|
|
4859
|
+
"preToolUse",
|
|
4860
|
+
"postToolUse",
|
|
4861
|
+
"permissionRequest",
|
|
4862
|
+
"beforeSubmitPrompt",
|
|
4863
|
+
"sessionStart",
|
|
4864
|
+
"sessionEnd",
|
|
4865
|
+
"preCompact",
|
|
4866
|
+
"stop",
|
|
4867
|
+
"subagentStop",
|
|
4868
|
+
"notification",
|
|
4869
|
+
"setup"
|
|
4870
|
+
];
|
|
4871
|
+
const CANONICAL_TO_CORTEXCODE_EVENT_NAMES = {
|
|
4872
|
+
preToolUse: "PreToolUse",
|
|
4873
|
+
postToolUse: "PostToolUse",
|
|
4874
|
+
permissionRequest: "PermissionRequest",
|
|
4875
|
+
beforeSubmitPrompt: "UserPromptSubmit",
|
|
4876
|
+
sessionStart: "SessionStart",
|
|
4877
|
+
sessionEnd: "SessionEnd",
|
|
4878
|
+
preCompact: "PreCompact",
|
|
4879
|
+
stop: "Stop",
|
|
4880
|
+
subagentStop: "SubagentStop",
|
|
4881
|
+
notification: "Notification",
|
|
4882
|
+
setup: "Setup"
|
|
4883
|
+
};
|
|
4884
|
+
const CORTEXCODE_TO_CANONICAL_EVENT_NAMES = Object.fromEntries(Object.entries(CANONICAL_TO_CORTEXCODE_EVENT_NAMES).map(([k, v]) => [v, k]));
|
|
4885
|
+
/**
|
|
4756
4886
|
* Hook events supported by Hermes Agent's native Shell Hooks system.
|
|
4757
4887
|
*
|
|
4758
4888
|
* Hermes validates hook events against a fixed `VALID_HOOKS` set — 37 entries as
|
|
@@ -4905,6 +5035,9 @@ const HooksConfigSchema = z.looseObject({
|
|
|
4905
5035
|
grokcli: z.optional(z.looseObject({ hooks: z.optional(hooksRecordSchema) })),
|
|
4906
5036
|
"kimi-code": z.optional(z.looseObject({ hooks: z.optional(hooksRecordSchema) })),
|
|
4907
5037
|
zcode: z.optional(z.looseObject({ hooks: z.optional(hooksRecordSchema) })),
|
|
5038
|
+
bob: z.optional(z.looseObject({ hooks: z.optional(hooksRecordSchema) })),
|
|
5039
|
+
cortexcode: z.optional(z.looseObject({ hooks: z.optional(hooksRecordSchema) })),
|
|
5040
|
+
tabnine: z.optional(z.looseObject({ hooks: z.optional(hooksRecordSchema) })),
|
|
4908
5041
|
qwencode: z.optional(z.looseObject({
|
|
4909
5042
|
hooks: z.optional(hooksRecordSchema),
|
|
4910
5043
|
disableAllHooks: z.optional(z.boolean())
|
|
@@ -5906,6 +6039,7 @@ const RulesyncMcpFileSchema = z.looseObject({
|
|
|
5906
6039
|
"antigravity-ide": z.optional(toolScopedMcpSchema),
|
|
5907
6040
|
aiassistant: z.optional(toolScopedMcpSchema),
|
|
5908
6041
|
augmentcode: z.optional(toolScopedMcpSchema),
|
|
6042
|
+
bob: z.optional(toolScopedMcpSchema),
|
|
5909
6043
|
claudecode: z.optional(toolScopedMcpSchema),
|
|
5910
6044
|
cline: z.optional(toolScopedMcpSchema),
|
|
5911
6045
|
codexcli: z.optional(toolScopedMcpSchema),
|
|
@@ -5927,6 +6061,8 @@ const RulesyncMcpFileSchema = z.looseObject({
|
|
|
5927
6061
|
reasonix: z.optional(toolScopedMcpSchema),
|
|
5928
6062
|
roo: z.optional(toolScopedMcpSchema),
|
|
5929
6063
|
rovodev: z.optional(toolScopedMcpSchema),
|
|
6064
|
+
cortexcode: z.optional(toolScopedMcpSchema),
|
|
6065
|
+
tabnine: z.optional(toolScopedMcpSchema),
|
|
5930
6066
|
takt: z.optional(toolScopedMcpSchema),
|
|
5931
6067
|
vibe: z.optional(toolScopedMcpSchema),
|
|
5932
6068
|
warp: z.optional(toolScopedMcpSchema),
|
|
@@ -6748,6 +6884,27 @@ const QwencodePermissionsOverrideSchema = z.looseObject({
|
|
|
6748
6884
|
autoMode: z.optional(z.looseObject({}))
|
|
6749
6885
|
});
|
|
6750
6886
|
/**
|
|
6887
|
+
* Tool-scoped override block for Tabnine CLI. The canonical `permission` block
|
|
6888
|
+
* drives only the `tools.allowed` / `tools.exclude` lists of
|
|
6889
|
+
* `.tabnine/agent/settings.json`; the other `tools.*` keys (`core`,
|
|
6890
|
+
* `enableCoaching`, `useRipgrep`, `shell.*`, `enableWebTools`, ...) and the
|
|
6891
|
+
* `general.defaultApprovalMode` switch have no canonical category, so they are
|
|
6892
|
+
* authored here, deep-merged into the settings file, and emitted only for
|
|
6893
|
+
* Tabnine. Import lifts the entries rulesync cannot map back onto a canonical
|
|
6894
|
+
* category into this block so a regenerate does not drop them. Kept
|
|
6895
|
+
* `looseObject` (verbatim passthrough). Both project and global scope are
|
|
6896
|
+
* supported.
|
|
6897
|
+
*
|
|
6898
|
+
* @example
|
|
6899
|
+
* { "tools": { "core": ["read_file", "run_shell_command(git)"] },
|
|
6900
|
+
* "general": { "defaultApprovalMode": "plan" } }
|
|
6901
|
+
*/
|
|
6902
|
+
const TabninePermissionsOverrideSchema = z.looseObject({
|
|
6903
|
+
permission: z.optional(ToolScopedPermissionSchema),
|
|
6904
|
+
tools: z.optional(z.looseObject({})),
|
|
6905
|
+
general: z.optional(z.looseObject({}))
|
|
6906
|
+
});
|
|
6907
|
+
/**
|
|
6751
6908
|
* Tool-scoped override block for Reasonix. Reasonix has security axes orthogonal
|
|
6752
6909
|
* to per-tool allow/ask/deny with no canonical category — the `[sandbox]`
|
|
6753
6910
|
* enforcement table (`workspace_root`, `allow_write`, `forbid_read`, `bash`,
|
|
@@ -7508,6 +7665,7 @@ const PermissionsConfigSchema = z.looseObject({
|
|
|
7508
7665
|
factorydroid: z.optional(FactorydroidPermissionsOverrideSchema),
|
|
7509
7666
|
warp: z.optional(WarpPermissionsOverrideSchema),
|
|
7510
7667
|
junie: z.optional(JuniePermissionsOverrideSchema),
|
|
7668
|
+
tabnine: z.optional(TabninePermissionsOverrideSchema),
|
|
7511
7669
|
takt: z.optional(TaktPermissionsOverrideSchema),
|
|
7512
7670
|
amp: z.optional(AmpPermissionsOverrideSchema),
|
|
7513
7671
|
"antigravity-cli": z.optional(AntigravityCliPermissionsOverrideSchema),
|
|
@@ -9155,6 +9313,7 @@ const RulesyncSkillFrontmatterSchema = z.looseObject({
|
|
|
9155
9313
|
})),
|
|
9156
9314
|
cline: z.optional(z.looseObject({})),
|
|
9157
9315
|
roo: z.optional(z.looseObject({})),
|
|
9316
|
+
bob: z.optional(z.looseObject({})),
|
|
9158
9317
|
amp: z.optional(z.looseObject({})),
|
|
9159
9318
|
devin: z.optional(z.looseObject({
|
|
9160
9319
|
"argument-hint": z.optional(z.string()),
|
|
@@ -9232,6 +9391,8 @@ const RulesyncSkillFrontmatterSchema = z.looseObject({
|
|
|
9232
9391
|
"user-invocable": z.optional(z.boolean()),
|
|
9233
9392
|
"allowed-tools": z.optional(z.union([z.string(), z.array(z.string())]))
|
|
9234
9393
|
})),
|
|
9394
|
+
cortexcode: z.optional(z.looseObject({})),
|
|
9395
|
+
tabnine: z.optional(z.looseObject({})),
|
|
9235
9396
|
takt: z.optional(z.looseObject({
|
|
9236
9397
|
name: z.optional(z.string()),
|
|
9237
9398
|
extends: z.optional(z.string())
|
|
@@ -12794,6 +12955,53 @@ const SHARED_CONFIG_OWNERSHIP = {
|
|
|
12794
12955
|
},
|
|
12795
12956
|
".zcode/config.json": ZCODE_WORKSPACE_CONFIG_DECLARATION,
|
|
12796
12957
|
".zcode/cli/config.json": ZCODE_USER_CONFIG_DECLARATION,
|
|
12958
|
+
".bob/settings.json": {
|
|
12959
|
+
format: "json",
|
|
12960
|
+
invalidRootPolicy: "error",
|
|
12961
|
+
features: { hooks: {
|
|
12962
|
+
kind: "replace-owned-keys",
|
|
12963
|
+
ownedKeys: ["hooks"]
|
|
12964
|
+
} }
|
|
12965
|
+
},
|
|
12966
|
+
".bob/settings/settings.json": {
|
|
12967
|
+
format: "json",
|
|
12968
|
+
invalidRootPolicy: "error",
|
|
12969
|
+
features: { hooks: {
|
|
12970
|
+
kind: "replace-owned-keys",
|
|
12971
|
+
ownedKeys: ["hooks"]
|
|
12972
|
+
} }
|
|
12973
|
+
},
|
|
12974
|
+
".cortex/settings.json": {
|
|
12975
|
+
format: "json",
|
|
12976
|
+
invalidRootPolicy: "error",
|
|
12977
|
+
features: { hooks: {
|
|
12978
|
+
kind: "replace-owned-keys",
|
|
12979
|
+
ownedKeys: ["hooks"]
|
|
12980
|
+
} }
|
|
12981
|
+
},
|
|
12982
|
+
".snowflake/cortex/hooks.json": {
|
|
12983
|
+
format: "json",
|
|
12984
|
+
invalidRootPolicy: "error",
|
|
12985
|
+
features: { hooks: {
|
|
12986
|
+
kind: "replace-owned-keys",
|
|
12987
|
+
ownedKeys: ["hooks"]
|
|
12988
|
+
} }
|
|
12989
|
+
},
|
|
12990
|
+
".tabnine/agent/settings.json": {
|
|
12991
|
+
format: "json",
|
|
12992
|
+
invalidRootPolicy: "error",
|
|
12993
|
+
features: {
|
|
12994
|
+
mcp: {
|
|
12995
|
+
kind: "replace-owned-keys",
|
|
12996
|
+
ownedKeys: ["mcpServers"]
|
|
12997
|
+
},
|
|
12998
|
+
hooks: {
|
|
12999
|
+
kind: "replace-owned-keys",
|
|
13000
|
+
ownedKeys: ["hooks"]
|
|
13001
|
+
},
|
|
13002
|
+
permissions: { kind: "deep-merge" }
|
|
13003
|
+
}
|
|
13004
|
+
},
|
|
12797
13005
|
".kiro/agents/default.json": {
|
|
12798
13006
|
format: "json",
|
|
12799
13007
|
features: {
|
|
@@ -14486,6 +14694,140 @@ var AugmentcodeCommand = class AugmentcodeCommand extends ToolCommand {
|
|
|
14486
14694
|
}
|
|
14487
14695
|
};
|
|
14488
14696
|
//#endregion
|
|
14697
|
+
//#region src/constants/bob-paths.ts
|
|
14698
|
+
const BOB_DIR = ".bob";
|
|
14699
|
+
const BOB_RULE_FILE_NAME = "AGENTS.md";
|
|
14700
|
+
join(BOB_DIR, "rules");
|
|
14701
|
+
const BOB_IGNORE_FILE_NAME = ".bobignore";
|
|
14702
|
+
const BOB_MCP_FILE_NAME = "mcp.json";
|
|
14703
|
+
const BOB_COMMANDS_DIR_PATH = join(BOB_DIR, "commands");
|
|
14704
|
+
const BOB_SKILLS_DIR_PATH = join(BOB_DIR, "skills");
|
|
14705
|
+
const BOB_SETTINGS_FILE_NAME = "settings.json";
|
|
14706
|
+
const BOB_GLOBAL_SETTINGS_DIR_PATH = join(BOB_DIR, "settings");
|
|
14707
|
+
//#endregion
|
|
14708
|
+
//#region src/features/commands/bob-command.ts
|
|
14709
|
+
/**
|
|
14710
|
+
* IBM Bob slash commands are Markdown files under `<project>/.bob/commands/`
|
|
14711
|
+
* (project scope) and `~/.bob/commands/` (user scope), invoked from the chat
|
|
14712
|
+
* input with `/`. The file name is the command name; the optional
|
|
14713
|
+
* `description` / `argument-hint` frontmatter keys are the same ones the
|
|
14714
|
+
* Claude-Code-lineage tools use, and the body is the prompt Bob runs.
|
|
14715
|
+
*
|
|
14716
|
+
* @see https://bob.ibm.com/docs/ide/features/slash-commands
|
|
14717
|
+
* @see https://bob.ibm.com/docs/shell/features/slash-commands
|
|
14718
|
+
*/
|
|
14719
|
+
const BobCommandFrontmatterSchema = z.looseObject({
|
|
14720
|
+
description: z.optional(z.string()),
|
|
14721
|
+
"argument-hint": z.optional(z.string())
|
|
14722
|
+
});
|
|
14723
|
+
var BobCommand = class BobCommand extends ToolCommand {
|
|
14724
|
+
frontmatter;
|
|
14725
|
+
body;
|
|
14726
|
+
constructor({ frontmatter, body, ...rest }) {
|
|
14727
|
+
if (rest.validate) {
|
|
14728
|
+
const result = BobCommandFrontmatterSchema.safeParse(frontmatter);
|
|
14729
|
+
if (!result.success) throw new Error(`Invalid frontmatter in ${join(rest.relativeDirPath, rest.relativeFilePath)}: ${formatError(result.error)}`);
|
|
14730
|
+
}
|
|
14731
|
+
super({
|
|
14732
|
+
...rest,
|
|
14733
|
+
fileContent: stringifyFrontmatter(body, frontmatter)
|
|
14734
|
+
});
|
|
14735
|
+
this.frontmatter = frontmatter;
|
|
14736
|
+
this.body = body;
|
|
14737
|
+
}
|
|
14738
|
+
static getSettablePaths(_options = {}) {
|
|
14739
|
+
return { relativeDirPath: BOB_COMMANDS_DIR_PATH };
|
|
14740
|
+
}
|
|
14741
|
+
getBody() {
|
|
14742
|
+
return this.body;
|
|
14743
|
+
}
|
|
14744
|
+
getFrontmatter() {
|
|
14745
|
+
return this.frontmatter;
|
|
14746
|
+
}
|
|
14747
|
+
toRulesyncCommand() {
|
|
14748
|
+
const { description, ...restFields } = this.frontmatter;
|
|
14749
|
+
const rulesyncFrontmatter = {
|
|
14750
|
+
targets: ["*"],
|
|
14751
|
+
description,
|
|
14752
|
+
...Object.keys(restFields).length > 0 && { bob: restFields }
|
|
14753
|
+
};
|
|
14754
|
+
const fileContent = stringifyFrontmatter(this.body, rulesyncFrontmatter);
|
|
14755
|
+
return new RulesyncCommand({
|
|
14756
|
+
outputRoot: ".",
|
|
14757
|
+
frontmatter: rulesyncFrontmatter,
|
|
14758
|
+
body: this.body,
|
|
14759
|
+
relativeDirPath: RulesyncCommand.getSettablePaths().relativeDirPath,
|
|
14760
|
+
relativeFilePath: this.relativeFilePath,
|
|
14761
|
+
fileContent,
|
|
14762
|
+
validate: true
|
|
14763
|
+
});
|
|
14764
|
+
}
|
|
14765
|
+
static fromRulesyncCommand({ outputRoot = process.cwd(), rulesyncCommand, validate = true, global = false }) {
|
|
14766
|
+
const rulesyncFrontmatter = rulesyncCommand.getFrontmatter();
|
|
14767
|
+
const bobFields = rulesyncFrontmatter.bob ?? {};
|
|
14768
|
+
const bobFrontmatter = {
|
|
14769
|
+
description: rulesyncFrontmatter.description,
|
|
14770
|
+
...bobFields
|
|
14771
|
+
};
|
|
14772
|
+
const body = rulesyncCommand.getBody();
|
|
14773
|
+
const paths = this.getSettablePaths({ global });
|
|
14774
|
+
return new BobCommand({
|
|
14775
|
+
outputRoot,
|
|
14776
|
+
frontmatter: bobFrontmatter,
|
|
14777
|
+
body,
|
|
14778
|
+
relativeDirPath: paths.relativeDirPath,
|
|
14779
|
+
relativeFilePath: rulesyncCommand.getRelativeFilePath(),
|
|
14780
|
+
validate
|
|
14781
|
+
});
|
|
14782
|
+
}
|
|
14783
|
+
validate() {
|
|
14784
|
+
if (!this.frontmatter) return {
|
|
14785
|
+
success: true,
|
|
14786
|
+
error: null
|
|
14787
|
+
};
|
|
14788
|
+
const result = BobCommandFrontmatterSchema.safeParse(this.frontmatter);
|
|
14789
|
+
if (result.success) return {
|
|
14790
|
+
success: true,
|
|
14791
|
+
error: null
|
|
14792
|
+
};
|
|
14793
|
+
return {
|
|
14794
|
+
success: false,
|
|
14795
|
+
error: /* @__PURE__ */ new Error(`Invalid frontmatter in ${join(this.relativeDirPath, this.relativeFilePath)}: ${formatError(result.error)}`)
|
|
14796
|
+
};
|
|
14797
|
+
}
|
|
14798
|
+
static isTargetedByRulesyncCommand(rulesyncCommand) {
|
|
14799
|
+
return this.isTargetedByRulesyncCommandDefault({
|
|
14800
|
+
rulesyncCommand,
|
|
14801
|
+
toolTarget: "bob"
|
|
14802
|
+
});
|
|
14803
|
+
}
|
|
14804
|
+
static async fromFile({ outputRoot = process.cwd(), relativeFilePath, validate = true, global = false }) {
|
|
14805
|
+
const paths = this.getSettablePaths({ global });
|
|
14806
|
+
const filePath = join(outputRoot, paths.relativeDirPath, relativeFilePath);
|
|
14807
|
+
const { frontmatter, body: content } = parseFrontmatter(await readFileContent(filePath), filePath);
|
|
14808
|
+
const result = BobCommandFrontmatterSchema.safeParse(frontmatter);
|
|
14809
|
+
if (!result.success) throw new Error(`Invalid frontmatter in ${filePath}: ${formatError(result.error)}`);
|
|
14810
|
+
return new BobCommand({
|
|
14811
|
+
outputRoot,
|
|
14812
|
+
relativeDirPath: paths.relativeDirPath,
|
|
14813
|
+
relativeFilePath,
|
|
14814
|
+
frontmatter: result.data,
|
|
14815
|
+
body: content.trim(),
|
|
14816
|
+
validate
|
|
14817
|
+
});
|
|
14818
|
+
}
|
|
14819
|
+
static forDeletion({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath }) {
|
|
14820
|
+
return new BobCommand({
|
|
14821
|
+
outputRoot,
|
|
14822
|
+
relativeDirPath,
|
|
14823
|
+
relativeFilePath,
|
|
14824
|
+
frontmatter: { description: "" },
|
|
14825
|
+
body: "",
|
|
14826
|
+
validate: false
|
|
14827
|
+
});
|
|
14828
|
+
}
|
|
14829
|
+
};
|
|
14830
|
+
//#endregion
|
|
14489
14831
|
//#region src/features/commands/claudecode-command.ts
|
|
14490
14832
|
const ClaudecodeCommandFrontmatterSchema = z.looseObject({
|
|
14491
14833
|
description: z.optional(z.string()),
|
|
@@ -18212,6 +18554,193 @@ var RovodevPromptsManifest = class extends ToolFile {
|
|
|
18212
18554
|
}
|
|
18213
18555
|
};
|
|
18214
18556
|
//#endregion
|
|
18557
|
+
//#region src/constants/tabnine-paths.ts
|
|
18558
|
+
const TABNINE_DIR = ".tabnine";
|
|
18559
|
+
const TABNINE_AGENT_DIR_PATH = join(TABNINE_DIR, "agent");
|
|
18560
|
+
const TABNINE_RULE_FILE_NAME = "TABNINE.md";
|
|
18561
|
+
join(TABNINE_DIR, "guidelines");
|
|
18562
|
+
const TABNINE_IGNORE_FILE_NAME = ".tabnineignore";
|
|
18563
|
+
const TABNINE_SETTINGS_FILE_NAME = "settings.json";
|
|
18564
|
+
const TABNINE_COMMANDS_DIR_PATH = join(TABNINE_AGENT_DIR_PATH, "commands");
|
|
18565
|
+
const TABNINE_AGENTS_DIR_PATH = join(TABNINE_AGENT_DIR_PATH, "agents");
|
|
18566
|
+
const TABNINE_SKILLS_DIR_PATH = join(TABNINE_AGENT_DIR_PATH, "skills");
|
|
18567
|
+
//#endregion
|
|
18568
|
+
//#region src/features/commands/tabnine-command.ts
|
|
18569
|
+
const TabnineCommandFrontmatterSchema = z.looseObject({
|
|
18570
|
+
description: z.optional(z.string()),
|
|
18571
|
+
prompt: z.string()
|
|
18572
|
+
});
|
|
18573
|
+
/**
|
|
18574
|
+
* Translate rulesync universal command syntax (Claude Code compatible) into
|
|
18575
|
+
* Tabnine CLI's native syntax. See docs/reference/command-syntax.md. Tabnine
|
|
18576
|
+
* CLI is a Gemini CLI derivative and documents the same placeholders:
|
|
18577
|
+
* `{{args}}` for the user's arguments and `!{cmd}` for shell injection. Its
|
|
18578
|
+
* `@{path}` file-injection form has no universal counterpart and passes
|
|
18579
|
+
* through untouched in both directions.
|
|
18580
|
+
* @see https://docs.tabnine.com/main/getting-started/tabnine-cli/features/commands
|
|
18581
|
+
*
|
|
18582
|
+
* Replacement order:
|
|
18583
|
+
* 1. `` !`cmd` `` → `!{cmd}` (backtick shell expansion → brace form)
|
|
18584
|
+
* 2. `$ARGUMENTS` → `{{args}}` (handled after step 1 so that
|
|
18585
|
+
* `` !`echo $ARGUMENTS` `` survives as `!{echo {{args}}}` rather than
|
|
18586
|
+
* requiring two passes).
|
|
18587
|
+
*
|
|
18588
|
+
* `$ARGUMENTS\b` uses a trailing word boundary so `$ARGUMENTSx` and
|
|
18589
|
+
* `$ARGUMENTS_FOO` are left alone, while `$ARGUMENTS-foo` (hyphen is not a
|
|
18590
|
+
* word char) is rewritten. There is no leading anchor, so `prefix$ARGUMENTS`
|
|
18591
|
+
* is rewritten to `prefix{{args}}`.
|
|
18592
|
+
*
|
|
18593
|
+
* Bodies that already contain Tabnine-native forms (`{{args}}` or `!{cmd}`)
|
|
18594
|
+
* are left untouched, which gives us the documented "we do not re-translate
|
|
18595
|
+
* already-Tabnine-native forms" property.
|
|
18596
|
+
*/
|
|
18597
|
+
function translateRulesyncBodyToTabnine(body) {
|
|
18598
|
+
return body.replace(/!`([^`\n]+)`/g, "!{$1}").replace(/\$ARGUMENTS\b/g, "{{args}}");
|
|
18599
|
+
}
|
|
18600
|
+
/**
|
|
18601
|
+
* Inverse of {@link translateRulesyncBodyToTabnine}, used when importing a
|
|
18602
|
+
* Tabnine CLI command file back into rulesync's universal syntax.
|
|
18603
|
+
*
|
|
18604
|
+
* Replacement order is intentionally inverted from the forward direction:
|
|
18605
|
+
* 1. `{{args}}` → `$ARGUMENTS` (handled first)
|
|
18606
|
+
* 2. `!{cmd}` → `` !`cmd` `` (handled second, with a non-greedy body
|
|
18607
|
+
* `[^}\n]+?`)
|
|
18608
|
+
*
|
|
18609
|
+
* Doing `{{args}}` first ensures that nested forms like
|
|
18610
|
+
* `!{echo {{args}}}` round-trip back to `` !`echo $ARGUMENTS` `` in a single
|
|
18611
|
+
* pass: the inner `{{args}}` is rewritten to `$ARGUMENTS`, and then the
|
|
18612
|
+
* non-greedy `!{...}` match consumes the smallest possible body.
|
|
18613
|
+
*/
|
|
18614
|
+
function translateTabnineBodyToRulesync(body) {
|
|
18615
|
+
return body.replace(/\{\{\s*args\s*\}\}/g, "$ARGUMENTS").replace(/!\{([^}\n]+?)\}/g, "!`$1`");
|
|
18616
|
+
}
|
|
18617
|
+
/**
|
|
18618
|
+
* Tabnine CLI custom slash commands: TOML files with `description` and
|
|
18619
|
+
* `prompt` under `<project>/.tabnine/agent/commands/` (project) and
|
|
18620
|
+
* `~/.tabnine/agent/commands/` (user). The relative path names the command:
|
|
18621
|
+
* `review.toml` → `/review`, `code/review.toml` → `/code:review`.
|
|
18622
|
+
* @see https://docs.tabnine.com/main/getting-started/tabnine-cli/features/commands
|
|
18623
|
+
*/
|
|
18624
|
+
var TabnineCommand = class TabnineCommand extends ToolCommand {
|
|
18625
|
+
frontmatter;
|
|
18626
|
+
body;
|
|
18627
|
+
constructor(params) {
|
|
18628
|
+
super(params);
|
|
18629
|
+
const parsed = this.parseTomlContent(this.fileContent);
|
|
18630
|
+
this.frontmatter = parsed;
|
|
18631
|
+
this.body = parsed.prompt;
|
|
18632
|
+
}
|
|
18633
|
+
static getSettablePaths(_options = {}) {
|
|
18634
|
+
return { relativeDirPath: TABNINE_COMMANDS_DIR_PATH };
|
|
18635
|
+
}
|
|
18636
|
+
parseTomlContent(content) {
|
|
18637
|
+
try {
|
|
18638
|
+
const parsed = parse$1(content);
|
|
18639
|
+
const result = TabnineCommandFrontmatterSchema.safeParse(parsed);
|
|
18640
|
+
if (!result.success) throw new Error(`Invalid frontmatter in ${join(this.relativeDirPath, this.relativeFilePath)}: ${formatError(result.error)}`);
|
|
18641
|
+
return {
|
|
18642
|
+
...result.data,
|
|
18643
|
+
description: result.data.description
|
|
18644
|
+
};
|
|
18645
|
+
} catch (error) {
|
|
18646
|
+
throw new Error(`Failed to parse TOML command file (${join(this.relativeDirPath, this.relativeFilePath)}): ${formatError(error)}`, { cause: error });
|
|
18647
|
+
}
|
|
18648
|
+
}
|
|
18649
|
+
getBody() {
|
|
18650
|
+
return this.body;
|
|
18651
|
+
}
|
|
18652
|
+
getFrontmatter() {
|
|
18653
|
+
return {
|
|
18654
|
+
description: this.frontmatter.description,
|
|
18655
|
+
prompt: this.frontmatter.prompt
|
|
18656
|
+
};
|
|
18657
|
+
}
|
|
18658
|
+
toRulesyncCommand() {
|
|
18659
|
+
const { description, prompt: _prompt, ...restFields } = this.frontmatter;
|
|
18660
|
+
const rulesyncFrontmatter = {
|
|
18661
|
+
targets: ["tabnine"],
|
|
18662
|
+
description,
|
|
18663
|
+
...Object.keys(restFields).length > 0 && { tabnine: restFields }
|
|
18664
|
+
};
|
|
18665
|
+
const universalBody = translateTabnineBodyToRulesync(this.body);
|
|
18666
|
+
const fileContent = stringifyFrontmatter(universalBody, rulesyncFrontmatter);
|
|
18667
|
+
return new RulesyncCommand({
|
|
18668
|
+
outputRoot: process.cwd(),
|
|
18669
|
+
frontmatter: rulesyncFrontmatter,
|
|
18670
|
+
body: universalBody,
|
|
18671
|
+
relativeDirPath: RulesyncCommand.getSettablePaths().relativeDirPath,
|
|
18672
|
+
relativeFilePath: this.relativeFilePath.replace(/\.toml$/, ".md"),
|
|
18673
|
+
fileContent,
|
|
18674
|
+
validate: true
|
|
18675
|
+
});
|
|
18676
|
+
}
|
|
18677
|
+
static fromRulesyncCommand({ outputRoot = process.cwd(), rulesyncCommand, validate = true, global = false }) {
|
|
18678
|
+
const rulesyncFrontmatter = rulesyncCommand.getFrontmatter();
|
|
18679
|
+
const tabnineFields = rulesyncFrontmatter.tabnine ?? {};
|
|
18680
|
+
const translatedPrompt = typeof tabnineFields.prompt === "string" ? "" : translateRulesyncBodyToTabnine(rulesyncCommand.getBody());
|
|
18681
|
+
const tabnineFrontmatter = {
|
|
18682
|
+
description: rulesyncFrontmatter.description,
|
|
18683
|
+
prompt: translatedPrompt,
|
|
18684
|
+
...tabnineFields
|
|
18685
|
+
};
|
|
18686
|
+
const tomlObject = {};
|
|
18687
|
+
if (tabnineFrontmatter.description !== void 0) tomlObject.description = tabnineFrontmatter.description;
|
|
18688
|
+
tomlObject.prompt = tabnineFrontmatter.prompt.endsWith("\n") ? tabnineFrontmatter.prompt : `${tabnineFrontmatter.prompt}\n`;
|
|
18689
|
+
const { description: _description, prompt: _prompt, ...extraFields } = tabnineFrontmatter;
|
|
18690
|
+
for (const [key, value] of Object.entries(omitPrototypePollutionKeys(extraFields))) if (value !== void 0) tomlObject[key] = value;
|
|
18691
|
+
const tomlContent = stringify(tomlObject);
|
|
18692
|
+
const paths = this.getSettablePaths({ global });
|
|
18693
|
+
return new TabnineCommand({
|
|
18694
|
+
outputRoot,
|
|
18695
|
+
relativeDirPath: paths.relativeDirPath,
|
|
18696
|
+
relativeFilePath: rulesyncCommand.getRelativeFilePath().replace(/\.md$/, ".toml"),
|
|
18697
|
+
fileContent: tomlContent,
|
|
18698
|
+
validate
|
|
18699
|
+
});
|
|
18700
|
+
}
|
|
18701
|
+
static async fromFile({ outputRoot = process.cwd(), relativeFilePath, validate = true, global = false }) {
|
|
18702
|
+
const paths = this.getSettablePaths({ global });
|
|
18703
|
+
const fileContent = await readFileContent(join(outputRoot, paths.relativeDirPath, relativeFilePath));
|
|
18704
|
+
return new TabnineCommand({
|
|
18705
|
+
outputRoot,
|
|
18706
|
+
relativeDirPath: paths.relativeDirPath,
|
|
18707
|
+
relativeFilePath,
|
|
18708
|
+
fileContent,
|
|
18709
|
+
validate
|
|
18710
|
+
});
|
|
18711
|
+
}
|
|
18712
|
+
validate() {
|
|
18713
|
+
try {
|
|
18714
|
+
this.parseTomlContent(this.fileContent);
|
|
18715
|
+
return {
|
|
18716
|
+
success: true,
|
|
18717
|
+
error: null
|
|
18718
|
+
};
|
|
18719
|
+
} catch (error) {
|
|
18720
|
+
return {
|
|
18721
|
+
success: false,
|
|
18722
|
+
error: error instanceof Error ? error : new Error(String(error))
|
|
18723
|
+
};
|
|
18724
|
+
}
|
|
18725
|
+
}
|
|
18726
|
+
static isTargetedByRulesyncCommand(rulesyncCommand) {
|
|
18727
|
+
return this.isTargetedByRulesyncCommandDefault({
|
|
18728
|
+
rulesyncCommand,
|
|
18729
|
+
toolTarget: "tabnine"
|
|
18730
|
+
});
|
|
18731
|
+
}
|
|
18732
|
+
static forDeletion({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath }) {
|
|
18733
|
+
return new TabnineCommand({
|
|
18734
|
+
outputRoot,
|
|
18735
|
+
relativeDirPath,
|
|
18736
|
+
relativeFilePath,
|
|
18737
|
+
fileContent: `description = ""
|
|
18738
|
+
prompt = ""`,
|
|
18739
|
+
validate: false
|
|
18740
|
+
});
|
|
18741
|
+
}
|
|
18742
|
+
};
|
|
18743
|
+
//#endregion
|
|
18215
18744
|
//#region src/features/takt-shared.ts
|
|
18216
18745
|
/**
|
|
18217
18746
|
* Shared utilities for all TAKT-* tool file classes.
|
|
@@ -18683,6 +19212,16 @@ const toolCommandFactories = /* @__PURE__ */ new Map([
|
|
|
18683
19212
|
matchAdditionalImportsByBasename: true
|
|
18684
19213
|
}
|
|
18685
19214
|
}],
|
|
19215
|
+
["bob", {
|
|
19216
|
+
class: BobCommand,
|
|
19217
|
+
meta: {
|
|
19218
|
+
extension: "md",
|
|
19219
|
+
supportsProject: true,
|
|
19220
|
+
supportsGlobal: true,
|
|
19221
|
+
isSimulated: false,
|
|
19222
|
+
supportsSubdirectory: true
|
|
19223
|
+
}
|
|
19224
|
+
}],
|
|
18686
19225
|
["claudecode", {
|
|
18687
19226
|
class: ClaudecodeCommand,
|
|
18688
19227
|
meta: {
|
|
@@ -18914,6 +19453,16 @@ const toolCommandFactories = /* @__PURE__ */ new Map([
|
|
|
18914
19453
|
supportsSubdirectory: false
|
|
18915
19454
|
}
|
|
18916
19455
|
}],
|
|
19456
|
+
["tabnine", {
|
|
19457
|
+
class: TabnineCommand,
|
|
19458
|
+
meta: {
|
|
19459
|
+
extension: "toml",
|
|
19460
|
+
supportsProject: true,
|
|
19461
|
+
supportsGlobal: true,
|
|
19462
|
+
isSimulated: false,
|
|
19463
|
+
supportsSubdirectory: true
|
|
19464
|
+
}
|
|
19465
|
+
}],
|
|
18917
19466
|
["takt", {
|
|
18918
19467
|
class: TaktCommand,
|
|
18919
19468
|
meta: {
|
|
@@ -20953,6 +21502,135 @@ var AugmentcodeHooks = class AugmentcodeHooks extends ToolHooks {
|
|
|
20953
21502
|
}
|
|
20954
21503
|
};
|
|
20955
21504
|
//#endregion
|
|
21505
|
+
//#region src/features/hooks/bob-hooks.ts
|
|
21506
|
+
const BOB_CONVERTER_CONFIG = {
|
|
21507
|
+
supportedEvents: BOB_HOOK_EVENTS,
|
|
21508
|
+
canonicalToToolEventNames: CANONICAL_TO_BOB_EVENT_NAMES,
|
|
21509
|
+
toolToCanonicalEventNames: BOB_TO_CANONICAL_EVENT_NAMES,
|
|
21510
|
+
projectDirVar: "",
|
|
21511
|
+
noMatcherEvents: /* @__PURE__ */ new Set([
|
|
21512
|
+
"sessionStart",
|
|
21513
|
+
"beforeSubmitPrompt",
|
|
21514
|
+
"stop"
|
|
21515
|
+
]),
|
|
21516
|
+
supportedHookTypes: /* @__PURE__ */ new Set(["command"]),
|
|
21517
|
+
wildcardMatcherMeansAll: true
|
|
21518
|
+
};
|
|
21519
|
+
/**
|
|
21520
|
+
* Single spelling of the settings.json codec/policy: fail closed on an
|
|
21521
|
+
* unparseable root rather than replacing the user's Bob settings with
|
|
21522
|
+
* generated output.
|
|
21523
|
+
*/
|
|
21524
|
+
function parseBobSettings(fileContent, filePath) {
|
|
21525
|
+
return parseSharedConfig({
|
|
21526
|
+
format: "json",
|
|
21527
|
+
fileContent,
|
|
21528
|
+
filePath,
|
|
21529
|
+
invalidRootPolicy: "error"
|
|
21530
|
+
});
|
|
21531
|
+
}
|
|
21532
|
+
/**
|
|
21533
|
+
* IBM Bob lifecycle hooks.
|
|
21534
|
+
*
|
|
21535
|
+
* Hooks live under the top-level `hooks` key of Bob's settings file —
|
|
21536
|
+
* `<project>/.bob/settings.json` (project scope) and
|
|
21537
|
+
* `~/.bob/settings/settings.json` (user scope) — in the Claude-Code shape:
|
|
21538
|
+
* `{ "<Event>": [{ "matcher"?: "<regex>", "hooks": [{ "type": "command",
|
|
21539
|
+
* "command": "...", "timeout"?: <seconds> }] }] }`. The file also holds
|
|
21540
|
+
* settings rulesync does not own, so generation merges the `hooks` key into it
|
|
21541
|
+
* (see `SHARED_CONFIG_OWNERSHIP`) instead of overwriting the file.
|
|
21542
|
+
*
|
|
21543
|
+
* @see https://bob.ibm.com/docs/ide/configuration/lifecycle-hooks
|
|
21544
|
+
*/
|
|
21545
|
+
var BobHooks = class BobHooks extends ToolHooks {
|
|
21546
|
+
constructor(params) {
|
|
21547
|
+
super({
|
|
21548
|
+
...params,
|
|
21549
|
+
fileContent: params.fileContent ?? "{}"
|
|
21550
|
+
});
|
|
21551
|
+
}
|
|
21552
|
+
isDeletable() {
|
|
21553
|
+
return false;
|
|
21554
|
+
}
|
|
21555
|
+
static getSettablePaths({ global = false } = {}) {
|
|
21556
|
+
return {
|
|
21557
|
+
relativeDirPath: global ? BOB_GLOBAL_SETTINGS_DIR_PATH : BOB_DIR,
|
|
21558
|
+
relativeFilePath: BOB_SETTINGS_FILE_NAME
|
|
21559
|
+
};
|
|
21560
|
+
}
|
|
21561
|
+
static async fromFile({ outputRoot = process.cwd(), validate = true, global = false }) {
|
|
21562
|
+
const paths = BobHooks.getSettablePaths({ global });
|
|
21563
|
+
const fileContent = await readFileContentOrNull(join(outputRoot, paths.relativeDirPath, paths.relativeFilePath)) ?? "{\"hooks\":{}}";
|
|
21564
|
+
return new BobHooks({
|
|
21565
|
+
outputRoot,
|
|
21566
|
+
relativeDirPath: paths.relativeDirPath,
|
|
21567
|
+
relativeFilePath: paths.relativeFilePath,
|
|
21568
|
+
fileContent,
|
|
21569
|
+
validate
|
|
21570
|
+
});
|
|
21571
|
+
}
|
|
21572
|
+
static async fromRulesyncHooks({ outputRoot = process.cwd(), rulesyncHooks, validate = true, global = false, logger }) {
|
|
21573
|
+
const paths = BobHooks.getSettablePaths({ global });
|
|
21574
|
+
const filePath = join(outputRoot, paths.relativeDirPath, paths.relativeFilePath);
|
|
21575
|
+
const existingContent = await readFileContentOrNull(filePath) ?? JSON.stringify({}, null, 2);
|
|
21576
|
+
const config = rulesyncHooks.getJson();
|
|
21577
|
+
const hooks = canonicalToToolHooks({
|
|
21578
|
+
config,
|
|
21579
|
+
toolOverrideHooks: config.bob?.hooks,
|
|
21580
|
+
converterConfig: BOB_CONVERTER_CONFIG,
|
|
21581
|
+
logger
|
|
21582
|
+
});
|
|
21583
|
+
const fileContent = applySharedConfigPatch({
|
|
21584
|
+
fileKey: sharedConfigFileKey(paths),
|
|
21585
|
+
feature: "hooks",
|
|
21586
|
+
existingContent,
|
|
21587
|
+
patch: { hooks },
|
|
21588
|
+
filePath,
|
|
21589
|
+
logger
|
|
21590
|
+
});
|
|
21591
|
+
return new BobHooks({
|
|
21592
|
+
outputRoot,
|
|
21593
|
+
relativeDirPath: paths.relativeDirPath,
|
|
21594
|
+
relativeFilePath: paths.relativeFilePath,
|
|
21595
|
+
fileContent,
|
|
21596
|
+
validate
|
|
21597
|
+
});
|
|
21598
|
+
}
|
|
21599
|
+
toRulesyncHooks({ logger } = {}) {
|
|
21600
|
+
const configPath = join(this.getRelativeDirPath(), this.getRelativeFilePath());
|
|
21601
|
+
let settings;
|
|
21602
|
+
try {
|
|
21603
|
+
settings = parseBobSettings(this.getFileContent(), configPath);
|
|
21604
|
+
} catch (error) {
|
|
21605
|
+
throw new Error(`Failed to parse Bob hooks content in ${configPath}: ${formatError(error)}`, { cause: error });
|
|
21606
|
+
}
|
|
21607
|
+
const hooks = toolHooksToCanonical({
|
|
21608
|
+
logger,
|
|
21609
|
+
hooks: settings.hooks,
|
|
21610
|
+
converterConfig: BOB_CONVERTER_CONFIG
|
|
21611
|
+
});
|
|
21612
|
+
return this.toRulesyncHooksDefault({ fileContent: JSON.stringify(buildImportedHooksConfig({
|
|
21613
|
+
hooks,
|
|
21614
|
+
overrideKey: "bob"
|
|
21615
|
+
}), null, 2) });
|
|
21616
|
+
}
|
|
21617
|
+
validate() {
|
|
21618
|
+
return {
|
|
21619
|
+
success: true,
|
|
21620
|
+
error: null
|
|
21621
|
+
};
|
|
21622
|
+
}
|
|
21623
|
+
static forDeletion({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath }) {
|
|
21624
|
+
return new BobHooks({
|
|
21625
|
+
outputRoot,
|
|
21626
|
+
relativeDirPath,
|
|
21627
|
+
relativeFilePath,
|
|
21628
|
+
fileContent: JSON.stringify({ hooks: {} }, null, 2),
|
|
21629
|
+
validate: false
|
|
21630
|
+
});
|
|
21631
|
+
}
|
|
21632
|
+
};
|
|
21633
|
+
//#endregion
|
|
20956
21634
|
//#region src/features/hooks/preserve-unowned-hook-commands.ts
|
|
20957
21635
|
/**
|
|
20958
21636
|
* Read the `hooks` value from a destination JSON file.
|
|
@@ -22518,6 +23196,151 @@ var CopilotcliHooks = class CopilotcliHooks extends ToolHooks {
|
|
|
22518
23196
|
}
|
|
22519
23197
|
};
|
|
22520
23198
|
//#endregion
|
|
23199
|
+
//#region src/constants/cortexcode-paths.ts
|
|
23200
|
+
const CORTEXCODE_DIR = ".cortex";
|
|
23201
|
+
const CORTEXCODE_GLOBAL_DIR_PATH = join(".snowflake", "cortex");
|
|
23202
|
+
const CORTEXCODE_RULE_FILE_NAME = "AGENTS.md";
|
|
23203
|
+
const CORTEXCODE_SETTINGS_FILE_NAME = "settings.json";
|
|
23204
|
+
const CORTEXCODE_GLOBAL_HOOKS_FILE_NAME = "hooks.json";
|
|
23205
|
+
const CORTEXCODE_MCP_FILE_NAME = "mcp.json";
|
|
23206
|
+
const CORTEXCODE_AGENTS_DIR_PATH = join(CORTEXCODE_DIR, "agents");
|
|
23207
|
+
const CORTEXCODE_GLOBAL_AGENTS_DIR_PATH = join(CORTEXCODE_GLOBAL_DIR_PATH, "agents");
|
|
23208
|
+
const CORTEXCODE_SKILLS_DIR_PATH = join(CORTEXCODE_DIR, "skills");
|
|
23209
|
+
const CORTEXCODE_GLOBAL_SKILLS_DIR_PATH = join(CORTEXCODE_GLOBAL_DIR_PATH, "skills");
|
|
23210
|
+
//#endregion
|
|
23211
|
+
//#region src/features/hooks/cortexcode-hooks.ts
|
|
23212
|
+
const CORTEXCODE_CONVERTER_CONFIG = {
|
|
23213
|
+
supportedEvents: CORTEXCODE_HOOK_EVENTS,
|
|
23214
|
+
canonicalToToolEventNames: CANONICAL_TO_CORTEXCODE_EVENT_NAMES,
|
|
23215
|
+
toolToCanonicalEventNames: CORTEXCODE_TO_CANONICAL_EVENT_NAMES,
|
|
23216
|
+
projectDirVar: "$CORTEX_PROJECT_DIR",
|
|
23217
|
+
prefixDotRelativeCommandsOnly: true,
|
|
23218
|
+
noMatcherEvents: /* @__PURE__ */ new Set(["beforeSubmitPrompt", "stop"]),
|
|
23219
|
+
supportedHookTypes: /* @__PURE__ */ new Set(["command", "prompt"]),
|
|
23220
|
+
booleanPassthroughFields: [{
|
|
23221
|
+
canonical: "enabled",
|
|
23222
|
+
tool: "enabled"
|
|
23223
|
+
}]
|
|
23224
|
+
};
|
|
23225
|
+
/**
|
|
23226
|
+
* Single spelling of the settings/hooks codec/policy: fail closed on an
|
|
23227
|
+
* unparseable root rather than replacing the user's Cortex Code settings with
|
|
23228
|
+
* generated output.
|
|
23229
|
+
*/
|
|
23230
|
+
function parseCortexcodeSettings(fileContent, filePath) {
|
|
23231
|
+
return parseSharedConfig({
|
|
23232
|
+
format: "json",
|
|
23233
|
+
fileContent,
|
|
23234
|
+
filePath,
|
|
23235
|
+
invalidRootPolicy: "error"
|
|
23236
|
+
});
|
|
23237
|
+
}
|
|
23238
|
+
/**
|
|
23239
|
+
* Snowflake Cortex Code hooks.
|
|
23240
|
+
*
|
|
23241
|
+
* Hooks live under the top-level `hooks` key of `<project>/.cortex/settings.json`
|
|
23242
|
+
* (project scope) and of the dedicated `~/.snowflake/cortex/hooks.json` (user
|
|
23243
|
+
* scope), both in the Claude-Code shape: `{ "<Event>": [{ "matcher"?:
|
|
23244
|
+
* "<regex>", "hooks": [{ "type": "command" | "prompt", ..., "timeout"?:
|
|
23245
|
+
* <seconds>, "enabled"?: <boolean> }] }] }`. The project file also holds
|
|
23246
|
+
* settings rulesync does not own, so generation merges the `hooks` key into
|
|
23247
|
+
* either file (see `SHARED_CONFIG_OWNERSHIP`) instead of overwriting it.
|
|
23248
|
+
*
|
|
23249
|
+
* @see https://docs.snowflake.com/en/user-guide/cortex-code/extensibility
|
|
23250
|
+
* @see https://docs.snowflake.com/en/user-guide/cortex-code/settings
|
|
23251
|
+
*/
|
|
23252
|
+
var CortexcodeHooks = class CortexcodeHooks extends ToolHooks {
|
|
23253
|
+
constructor(params) {
|
|
23254
|
+
super({
|
|
23255
|
+
...params,
|
|
23256
|
+
fileContent: params.fileContent ?? "{}"
|
|
23257
|
+
});
|
|
23258
|
+
}
|
|
23259
|
+
isDeletable() {
|
|
23260
|
+
return false;
|
|
23261
|
+
}
|
|
23262
|
+
static getSettablePaths({ global = false } = {}) {
|
|
23263
|
+
return global ? {
|
|
23264
|
+
relativeDirPath: CORTEXCODE_GLOBAL_DIR_PATH,
|
|
23265
|
+
relativeFilePath: CORTEXCODE_GLOBAL_HOOKS_FILE_NAME
|
|
23266
|
+
} : {
|
|
23267
|
+
relativeDirPath: CORTEXCODE_DIR,
|
|
23268
|
+
relativeFilePath: CORTEXCODE_SETTINGS_FILE_NAME
|
|
23269
|
+
};
|
|
23270
|
+
}
|
|
23271
|
+
static async fromFile({ outputRoot = process.cwd(), validate = true, global = false }) {
|
|
23272
|
+
const paths = CortexcodeHooks.getSettablePaths({ global });
|
|
23273
|
+
const fileContent = await readFileContentOrNull(join(outputRoot, paths.relativeDirPath, paths.relativeFilePath)) ?? "{\"hooks\":{}}";
|
|
23274
|
+
return new CortexcodeHooks({
|
|
23275
|
+
outputRoot,
|
|
23276
|
+
relativeDirPath: paths.relativeDirPath,
|
|
23277
|
+
relativeFilePath: paths.relativeFilePath,
|
|
23278
|
+
fileContent,
|
|
23279
|
+
validate
|
|
23280
|
+
});
|
|
23281
|
+
}
|
|
23282
|
+
static async fromRulesyncHooks({ outputRoot = process.cwd(), rulesyncHooks, validate = true, global = false, logger }) {
|
|
23283
|
+
const paths = CortexcodeHooks.getSettablePaths({ global });
|
|
23284
|
+
const filePath = join(outputRoot, paths.relativeDirPath, paths.relativeFilePath);
|
|
23285
|
+
const existingContent = await readFileContentOrNull(filePath) ?? JSON.stringify({}, null, 2);
|
|
23286
|
+
const config = rulesyncHooks.getJson();
|
|
23287
|
+
const hooks = canonicalToToolHooks({
|
|
23288
|
+
config,
|
|
23289
|
+
toolOverrideHooks: config.cortexcode?.hooks,
|
|
23290
|
+
converterConfig: CORTEXCODE_CONVERTER_CONFIG,
|
|
23291
|
+
logger
|
|
23292
|
+
});
|
|
23293
|
+
const fileContent = applySharedConfigPatch({
|
|
23294
|
+
fileKey: sharedConfigFileKey(paths),
|
|
23295
|
+
feature: "hooks",
|
|
23296
|
+
existingContent,
|
|
23297
|
+
patch: { hooks },
|
|
23298
|
+
filePath,
|
|
23299
|
+
logger
|
|
23300
|
+
});
|
|
23301
|
+
return new CortexcodeHooks({
|
|
23302
|
+
outputRoot,
|
|
23303
|
+
relativeDirPath: paths.relativeDirPath,
|
|
23304
|
+
relativeFilePath: paths.relativeFilePath,
|
|
23305
|
+
fileContent,
|
|
23306
|
+
validate
|
|
23307
|
+
});
|
|
23308
|
+
}
|
|
23309
|
+
toRulesyncHooks({ logger } = {}) {
|
|
23310
|
+
const configPath = join(this.getRelativeDirPath(), this.getRelativeFilePath());
|
|
23311
|
+
let settings;
|
|
23312
|
+
try {
|
|
23313
|
+
settings = parseCortexcodeSettings(this.getFileContent(), configPath);
|
|
23314
|
+
} catch (error) {
|
|
23315
|
+
throw new Error(`Failed to parse Cortex Code hooks content in ${configPath}: ${formatError(error)}`, { cause: error });
|
|
23316
|
+
}
|
|
23317
|
+
const hooks = toolHooksToCanonical({
|
|
23318
|
+
logger,
|
|
23319
|
+
hooks: settings.hooks,
|
|
23320
|
+
converterConfig: CORTEXCODE_CONVERTER_CONFIG
|
|
23321
|
+
});
|
|
23322
|
+
return this.toRulesyncHooksDefault({ fileContent: JSON.stringify(buildImportedHooksConfig({
|
|
23323
|
+
hooks,
|
|
23324
|
+
overrideKey: "cortexcode"
|
|
23325
|
+
}), null, 2) });
|
|
23326
|
+
}
|
|
23327
|
+
validate() {
|
|
23328
|
+
return {
|
|
23329
|
+
success: true,
|
|
23330
|
+
error: null
|
|
23331
|
+
};
|
|
23332
|
+
}
|
|
23333
|
+
static forDeletion({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath }) {
|
|
23334
|
+
return new CortexcodeHooks({
|
|
23335
|
+
outputRoot,
|
|
23336
|
+
relativeDirPath,
|
|
23337
|
+
relativeFilePath,
|
|
23338
|
+
fileContent: JSON.stringify({ hooks: {} }, null, 2),
|
|
23339
|
+
validate: false
|
|
23340
|
+
});
|
|
23341
|
+
}
|
|
23342
|
+
};
|
|
23343
|
+
//#endregion
|
|
22521
23344
|
//#region src/features/hooks/cursor-hooks.ts
|
|
22522
23345
|
var CursorHooks = class CursorHooks extends ToolHooks {
|
|
22523
23346
|
constructor(params) {
|
|
@@ -25745,6 +26568,270 @@ var ReasonixHooks = class ReasonixHooks extends ToolHooks {
|
|
|
25745
26568
|
}
|
|
25746
26569
|
};
|
|
25747
26570
|
//#endregion
|
|
26571
|
+
//#region src/features/hooks/tabnine-hooks.ts
|
|
26572
|
+
/**
|
|
26573
|
+
* Environment block safe to hand Tabnine for a hook. A tool rebuilds each
|
|
26574
|
+
* entry into `KEY=VALUE` for the spawned process, so a key holding `=`, a
|
|
26575
|
+
* control character or nothing at all names a different variable than it
|
|
26576
|
+
* appears to; such entries are dropped in both directions (with a warning on
|
|
26577
|
+
* export, where the value came from an authored `.rulesync/hooks.*`).
|
|
26578
|
+
*/
|
|
26579
|
+
function sanitizeEnv({ env, warn }) {
|
|
26580
|
+
if (env === null || env === void 0 || typeof env !== "object" || Array.isArray(env)) return;
|
|
26581
|
+
const result = {};
|
|
26582
|
+
for (const [key, value] of Object.entries(env)) {
|
|
26583
|
+
const unsafeKey = key === "" || key.includes("=") || CONTROL_CHARS.some((char) => key.includes(char));
|
|
26584
|
+
const unsafeValue = typeof value !== "string" || CONTROL_CHARS.some((char) => value.includes(char));
|
|
26585
|
+
if (unsafeKey || unsafeValue) {
|
|
26586
|
+
warn?.(`Tabnine CLI hook env entry ${JSON.stringify(key)} is not a safe KEY=VALUE pair; skipping it.`);
|
|
26587
|
+
continue;
|
|
26588
|
+
}
|
|
26589
|
+
result[key] = value;
|
|
26590
|
+
}
|
|
26591
|
+
return Object.keys(result).length > 0 ? result : void 0;
|
|
26592
|
+
}
|
|
26593
|
+
/**
|
|
26594
|
+
* Build one Tabnine hook configuration from a canonical command definition.
|
|
26595
|
+
* Canonical `timeout` is seconds (docs/reference/file-formats.md); Tabnine's is
|
|
26596
|
+
* milliseconds.
|
|
26597
|
+
* @see https://docs.tabnine.com/main/getting-started/tabnine-cli/features/hooks/configuration
|
|
26598
|
+
*/
|
|
26599
|
+
function canonicalDefToTabnineHook({ def, warn }) {
|
|
26600
|
+
return {
|
|
26601
|
+
type: "command",
|
|
26602
|
+
...compact({
|
|
26603
|
+
command: def.command,
|
|
26604
|
+
name: def.name,
|
|
26605
|
+
description: def.description,
|
|
26606
|
+
timeout: typeof def.timeout === "number" && Number.isFinite(def.timeout) ? Math.round(def.timeout * 1e3) : void 0,
|
|
26607
|
+
env: sanitizeEnv({
|
|
26608
|
+
env: def.env,
|
|
26609
|
+
warn
|
|
26610
|
+
})
|
|
26611
|
+
})
|
|
26612
|
+
};
|
|
26613
|
+
}
|
|
26614
|
+
/**
|
|
26615
|
+
* Convert the canonical hooks config to Tabnine's `hooks` map: PascalCase
|
|
26616
|
+
* event names, matcher groups with an optional `sequential` flag, and
|
|
26617
|
+
* command-only hook entries. Tabnine treats an omitted `matcher` as match-all
|
|
26618
|
+
* and compiles tool-event matchers as regular expressions, so the canonical
|
|
26619
|
+
* catch-all `"*"` (not a valid regex) is emitted as no matcher. Commands are
|
|
26620
|
+
* passed through verbatim; Tabnine expands `$TABNINE_PROJECT_DIR` itself.
|
|
26621
|
+
*/
|
|
26622
|
+
function canonicalToTabnineHooks({ config, logger }) {
|
|
26623
|
+
const warn = (message) => logger?.warn(message);
|
|
26624
|
+
const supported = new Set(TABNINE_HOOK_EVENTS);
|
|
26625
|
+
const sharedHooks = {};
|
|
26626
|
+
for (const [event, defs] of Object.entries(config.hooks)) if (supported.has(event)) sharedHooks[event] = defs;
|
|
26627
|
+
const effectiveHooks = {
|
|
26628
|
+
...sharedHooks,
|
|
26629
|
+
...config.tabnine?.hooks
|
|
26630
|
+
};
|
|
26631
|
+
const tabnine = {};
|
|
26632
|
+
for (const [eventName, definitions] of Object.entries(effectiveHooks)) {
|
|
26633
|
+
const tabnineEventName = lookupOwn({
|
|
26634
|
+
record: CANONICAL_TO_TABNINE_EVENT_NAMES,
|
|
26635
|
+
key: eventName
|
|
26636
|
+
}) ?? eventName;
|
|
26637
|
+
const byMatcher = /* @__PURE__ */ new Map();
|
|
26638
|
+
for (const def of definitions) {
|
|
26639
|
+
if ((def.type ?? "command") !== "command") continue;
|
|
26640
|
+
if (typeof def.command !== "string" || def.command === "") {
|
|
26641
|
+
warn(`Tabnine CLI hook on '${eventName}' has no 'command'; Tabnine would discard it, so it is skipped.`);
|
|
26642
|
+
continue;
|
|
26643
|
+
}
|
|
26644
|
+
const key = def.matcher === void 0 || def.matcher === "*" ? "" : def.matcher;
|
|
26645
|
+
const list = byMatcher.get(key);
|
|
26646
|
+
if (list) list.push(def);
|
|
26647
|
+
else byMatcher.set(key, [def]);
|
|
26648
|
+
}
|
|
26649
|
+
const entries = [];
|
|
26650
|
+
for (const [matcherKey, defs] of byMatcher) {
|
|
26651
|
+
const hooks = defs.map((def) => canonicalDefToTabnineHook({
|
|
26652
|
+
def,
|
|
26653
|
+
warn
|
|
26654
|
+
}));
|
|
26655
|
+
const sequential = defs.some((def) => def.sequential === true);
|
|
26656
|
+
const group = matcherKey ? {
|
|
26657
|
+
matcher: matcherKey,
|
|
26658
|
+
hooks
|
|
26659
|
+
} : { hooks };
|
|
26660
|
+
if (sequential) group.sequential = true;
|
|
26661
|
+
entries.push(group);
|
|
26662
|
+
}
|
|
26663
|
+
if (entries.length > 0) tabnine[tabnineEventName] = entries;
|
|
26664
|
+
}
|
|
26665
|
+
return tabnine;
|
|
26666
|
+
}
|
|
26667
|
+
/**
|
|
26668
|
+
* Tabnine hook configuration as stored in each matcher group's `hooks` array.
|
|
26669
|
+
* `z.looseObject` keeps fields added by future Tabnine releases.
|
|
26670
|
+
*/
|
|
26671
|
+
const TabnineHookEntrySchema = z.looseObject({
|
|
26672
|
+
type: z.optional(z.string()),
|
|
26673
|
+
command: z.optional(z.string()),
|
|
26674
|
+
name: z.optional(z.string()),
|
|
26675
|
+
description: z.optional(z.string()),
|
|
26676
|
+
timeout: z.optional(z.number()),
|
|
26677
|
+
env: z.optional(z.record(z.string(), z.string()))
|
|
26678
|
+
});
|
|
26679
|
+
const TabnineMatcherEntrySchema = z.looseObject({
|
|
26680
|
+
matcher: z.optional(z.string()),
|
|
26681
|
+
sequential: z.optional(z.boolean()),
|
|
26682
|
+
hooks: z.optional(z.array(TabnineHookEntrySchema))
|
|
26683
|
+
});
|
|
26684
|
+
function tabnineMatcherEntryToCanonical(entry) {
|
|
26685
|
+
const sequential = entry.sequential === true;
|
|
26686
|
+
const matcher = entry.matcher !== void 0 && entry.matcher !== null && entry.matcher !== "" ? entry.matcher : void 0;
|
|
26687
|
+
const defs = [];
|
|
26688
|
+
for (const hook of entry.hooks ?? []) {
|
|
26689
|
+
if (hook.type !== "command" || typeof hook.command !== "string" || hook.command === "") continue;
|
|
26690
|
+
defs.push({
|
|
26691
|
+
type: "command",
|
|
26692
|
+
...compact({
|
|
26693
|
+
command: hook.command,
|
|
26694
|
+
name: hook.name,
|
|
26695
|
+
description: hook.description,
|
|
26696
|
+
timeout: typeof hook.timeout === "number" && Number.isFinite(hook.timeout) ? hook.timeout / 1e3 : void 0,
|
|
26697
|
+
env: sanitizeEnv({ env: hook.env }),
|
|
26698
|
+
sequential: sequential ? true : void 0,
|
|
26699
|
+
matcher
|
|
26700
|
+
})
|
|
26701
|
+
});
|
|
26702
|
+
}
|
|
26703
|
+
return defs;
|
|
26704
|
+
}
|
|
26705
|
+
/**
|
|
26706
|
+
* Extract the `hooks` map of a Tabnine settings file into canonical form.
|
|
26707
|
+
*/
|
|
26708
|
+
function tabnineHooksToCanonical(tabnineHooks) {
|
|
26709
|
+
if (tabnineHooks === null || tabnineHooks === void 0 || typeof tabnineHooks !== "object") return {};
|
|
26710
|
+
const canonical = {};
|
|
26711
|
+
for (const [tabnineEventName, matcherEntries] of Object.entries(tabnineHooks)) {
|
|
26712
|
+
const eventName = lookupOwn({
|
|
26713
|
+
record: TABNINE_TO_CANONICAL_EVENT_NAMES,
|
|
26714
|
+
key: tabnineEventName
|
|
26715
|
+
}) ?? tabnineEventName;
|
|
26716
|
+
if (!Array.isArray(matcherEntries)) continue;
|
|
26717
|
+
const defs = [];
|
|
26718
|
+
for (const rawEntry of matcherEntries) {
|
|
26719
|
+
const parseResult = TabnineMatcherEntrySchema.safeParse(rawEntry);
|
|
26720
|
+
if (!parseResult.success) continue;
|
|
26721
|
+
defs.push(...tabnineMatcherEntryToCanonical(parseResult.data));
|
|
26722
|
+
}
|
|
26723
|
+
if (defs.length > 0) canonical[eventName] = defs;
|
|
26724
|
+
}
|
|
26725
|
+
return canonical;
|
|
26726
|
+
}
|
|
26727
|
+
/**
|
|
26728
|
+
* Fail closed on an unparseable settings root rather than replacing the
|
|
26729
|
+
* user's Tabnine settings with generated output.
|
|
26730
|
+
*/
|
|
26731
|
+
function parseTabnineSettings(fileContent, filePath) {
|
|
26732
|
+
return parseSharedConfig({
|
|
26733
|
+
format: "json",
|
|
26734
|
+
fileContent,
|
|
26735
|
+
filePath,
|
|
26736
|
+
invalidRootPolicy: "error"
|
|
26737
|
+
});
|
|
26738
|
+
}
|
|
26739
|
+
/**
|
|
26740
|
+
* Tabnine CLI hooks.
|
|
26741
|
+
*
|
|
26742
|
+
* Hooks live under the top-level `hooks` key of Tabnine's settings file —
|
|
26743
|
+
* `<project>/.tabnine/agent/settings.json` (project scope) and
|
|
26744
|
+
* `~/.tabnine/agent/settings.json` (user scope). The file also carries
|
|
26745
|
+
* `mcpServers`, `tools` and other user-managed settings, so generation merges
|
|
26746
|
+
* the `hooks` key into it (see `SHARED_CONFIG_OWNERSHIP`) instead of
|
|
26747
|
+
* overwriting the file. The sibling `hooksConfig` block (master toggle,
|
|
26748
|
+
* disabled list, notifications) is left to the user.
|
|
26749
|
+
*
|
|
26750
|
+
* @see https://docs.tabnine.com/main/getting-started/tabnine-cli/features/hooks
|
|
26751
|
+
* @see https://docs.tabnine.com/main/getting-started/tabnine-cli/features/hooks/configuration
|
|
26752
|
+
*/
|
|
26753
|
+
var TabnineHooks = class TabnineHooks extends ToolHooks {
|
|
26754
|
+
constructor(params) {
|
|
26755
|
+
super({
|
|
26756
|
+
...params,
|
|
26757
|
+
fileContent: params.fileContent ?? "{}"
|
|
26758
|
+
});
|
|
26759
|
+
}
|
|
26760
|
+
isDeletable() {
|
|
26761
|
+
return false;
|
|
26762
|
+
}
|
|
26763
|
+
static getSettablePaths(_options = {}) {
|
|
26764
|
+
return {
|
|
26765
|
+
relativeDirPath: TABNINE_AGENT_DIR_PATH,
|
|
26766
|
+
relativeFilePath: TABNINE_SETTINGS_FILE_NAME
|
|
26767
|
+
};
|
|
26768
|
+
}
|
|
26769
|
+
static async fromFile({ outputRoot = process.cwd(), validate = true, global = false }) {
|
|
26770
|
+
const paths = TabnineHooks.getSettablePaths({ global });
|
|
26771
|
+
const fileContent = await readFileContentOrNull(join(outputRoot, paths.relativeDirPath, paths.relativeFilePath)) ?? "{\"hooks\":{}}";
|
|
26772
|
+
return new TabnineHooks({
|
|
26773
|
+
outputRoot,
|
|
26774
|
+
relativeDirPath: paths.relativeDirPath,
|
|
26775
|
+
relativeFilePath: paths.relativeFilePath,
|
|
26776
|
+
fileContent,
|
|
26777
|
+
validate
|
|
26778
|
+
});
|
|
26779
|
+
}
|
|
26780
|
+
static async fromRulesyncHooks({ outputRoot = process.cwd(), rulesyncHooks, validate = true, global = false, logger }) {
|
|
26781
|
+
const paths = TabnineHooks.getSettablePaths({ global });
|
|
26782
|
+
const filePath = join(outputRoot, paths.relativeDirPath, paths.relativeFilePath);
|
|
26783
|
+
const existingContent = await readFileContentOrNull(filePath) ?? JSON.stringify({}, null, 2);
|
|
26784
|
+
const hooks = canonicalToTabnineHooks({
|
|
26785
|
+
config: rulesyncHooks.getJson(),
|
|
26786
|
+
logger
|
|
26787
|
+
});
|
|
26788
|
+
const fileContent = applySharedConfigPatch({
|
|
26789
|
+
fileKey: sharedConfigFileKey(paths),
|
|
26790
|
+
feature: "hooks",
|
|
26791
|
+
existingContent,
|
|
26792
|
+
patch: { hooks },
|
|
26793
|
+
filePath,
|
|
26794
|
+
logger
|
|
26795
|
+
});
|
|
26796
|
+
return new TabnineHooks({
|
|
26797
|
+
outputRoot,
|
|
26798
|
+
relativeDirPath: paths.relativeDirPath,
|
|
26799
|
+
relativeFilePath: paths.relativeFilePath,
|
|
26800
|
+
fileContent,
|
|
26801
|
+
validate
|
|
26802
|
+
});
|
|
26803
|
+
}
|
|
26804
|
+
toRulesyncHooks() {
|
|
26805
|
+
const configPath = join(this.getRelativeDirPath(), this.getRelativeFilePath());
|
|
26806
|
+
let settings;
|
|
26807
|
+
try {
|
|
26808
|
+
settings = parseTabnineSettings(this.getFileContent(), configPath);
|
|
26809
|
+
} catch (error) {
|
|
26810
|
+
throw new Error(`Failed to parse Tabnine CLI hooks content in ${configPath}: ${formatError(error)}`, { cause: error });
|
|
26811
|
+
}
|
|
26812
|
+
const hooks = tabnineHooksToCanonical(settings.hooks);
|
|
26813
|
+
return this.toRulesyncHooksDefault({ fileContent: JSON.stringify(buildImportedHooksConfig({
|
|
26814
|
+
hooks,
|
|
26815
|
+
overrideKey: "tabnine"
|
|
26816
|
+
}), null, 2) });
|
|
26817
|
+
}
|
|
26818
|
+
validate() {
|
|
26819
|
+
return {
|
|
26820
|
+
success: true,
|
|
26821
|
+
error: null
|
|
26822
|
+
};
|
|
26823
|
+
}
|
|
26824
|
+
static forDeletion({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath }) {
|
|
26825
|
+
return new TabnineHooks({
|
|
26826
|
+
outputRoot,
|
|
26827
|
+
relativeDirPath,
|
|
26828
|
+
relativeFilePath,
|
|
26829
|
+
fileContent: JSON.stringify({ hooks: {} }, null, 2),
|
|
26830
|
+
validate: false
|
|
26831
|
+
});
|
|
26832
|
+
}
|
|
26833
|
+
};
|
|
26834
|
+
//#endregion
|
|
25748
26835
|
//#region src/features/hooks/vibe-hooks.ts
|
|
25749
26836
|
const VIBE_DIR = ".vibe";
|
|
25750
26837
|
const VIBE_HOOKS_FILE_NAME = "hooks.toml";
|
|
@@ -26504,6 +27591,40 @@ const toolHooksFactories = /* @__PURE__ */ new Map([
|
|
|
26504
27591
|
supportedHookTypes: ["command"],
|
|
26505
27592
|
supportsMatcher: true
|
|
26506
27593
|
}],
|
|
27594
|
+
["bob", {
|
|
27595
|
+
class: BobHooks,
|
|
27596
|
+
meta: {
|
|
27597
|
+
supportsProject: true,
|
|
27598
|
+
supportsGlobal: true,
|
|
27599
|
+
supportsImport: true
|
|
27600
|
+
},
|
|
27601
|
+
supportedEvents: BOB_HOOK_EVENTS,
|
|
27602
|
+
supportedHookTypes: ["command"],
|
|
27603
|
+
supportsMatcher: true,
|
|
27604
|
+
matcherEvents: ["preToolUse", "postToolUse"]
|
|
27605
|
+
}],
|
|
27606
|
+
["cortexcode", {
|
|
27607
|
+
class: CortexcodeHooks,
|
|
27608
|
+
meta: {
|
|
27609
|
+
supportsProject: true,
|
|
27610
|
+
supportsGlobal: true,
|
|
27611
|
+
supportsImport: true
|
|
27612
|
+
},
|
|
27613
|
+
supportedEvents: CORTEXCODE_HOOK_EVENTS,
|
|
27614
|
+
supportedHookTypes: ["command", "prompt"],
|
|
27615
|
+
supportsMatcher: true,
|
|
27616
|
+
matcherEvents: [
|
|
27617
|
+
"preToolUse",
|
|
27618
|
+
"postToolUse",
|
|
27619
|
+
"permissionRequest",
|
|
27620
|
+
"sessionStart",
|
|
27621
|
+
"sessionEnd",
|
|
27622
|
+
"preCompact",
|
|
27623
|
+
"subagentStop",
|
|
27624
|
+
"notification",
|
|
27625
|
+
"setup"
|
|
27626
|
+
]
|
|
27627
|
+
}],
|
|
26507
27628
|
["junie", {
|
|
26508
27629
|
class: JunieHooks,
|
|
26509
27630
|
meta: {
|
|
@@ -26515,6 +27636,17 @@ const toolHooksFactories = /* @__PURE__ */ new Map([
|
|
|
26515
27636
|
supportedHookTypes: ["command"],
|
|
26516
27637
|
supportsMatcher: true
|
|
26517
27638
|
}],
|
|
27639
|
+
["tabnine", {
|
|
27640
|
+
class: TabnineHooks,
|
|
27641
|
+
meta: {
|
|
27642
|
+
supportsProject: true,
|
|
27643
|
+
supportsGlobal: true,
|
|
27644
|
+
supportsImport: true
|
|
27645
|
+
},
|
|
27646
|
+
supportedEvents: TABNINE_HOOK_EVENTS,
|
|
27647
|
+
supportedHookTypes: ["command"],
|
|
27648
|
+
supportsMatcher: true
|
|
27649
|
+
}],
|
|
26518
27650
|
["vibe", {
|
|
26519
27651
|
class: VibeHooks,
|
|
26520
27652
|
meta: {
|
|
@@ -26961,6 +28093,58 @@ var AugmentcodeIgnore = class AugmentcodeIgnore extends ToolIgnore {
|
|
|
26961
28093
|
}
|
|
26962
28094
|
};
|
|
26963
28095
|
//#endregion
|
|
28096
|
+
//#region src/features/ignore/bob-ignore.ts
|
|
28097
|
+
/**
|
|
28098
|
+
* BobIgnore represents the `.bobignore` file of IBM Bob.
|
|
28099
|
+
*
|
|
28100
|
+
* - File location: workspace root only (`.bobignore`); no user-scope variant
|
|
28101
|
+
* is documented, so the feature is project-only.
|
|
28102
|
+
* - Syntax: the same as `.gitignore`.
|
|
28103
|
+
* - Matching files are hidden from Bob's file reads and listings.
|
|
28104
|
+
*
|
|
28105
|
+
* @see https://bob.ibm.com/docs/ide/configuration/bobignore
|
|
28106
|
+
*/
|
|
28107
|
+
var BobIgnore = class BobIgnore extends ToolIgnore {
|
|
28108
|
+
static getSettablePaths() {
|
|
28109
|
+
return {
|
|
28110
|
+
relativeDirPath: ".",
|
|
28111
|
+
relativeFilePath: BOB_IGNORE_FILE_NAME
|
|
28112
|
+
};
|
|
28113
|
+
}
|
|
28114
|
+
toRulesyncIgnore() {
|
|
28115
|
+
return this.toRulesyncIgnoreDefault();
|
|
28116
|
+
}
|
|
28117
|
+
static fromRulesyncIgnore({ outputRoot = process.cwd(), rulesyncIgnore }) {
|
|
28118
|
+
const paths = this.getSettablePaths();
|
|
28119
|
+
return new BobIgnore({
|
|
28120
|
+
outputRoot,
|
|
28121
|
+
relativeDirPath: paths.relativeDirPath,
|
|
28122
|
+
relativeFilePath: paths.relativeFilePath,
|
|
28123
|
+
fileContent: rulesyncIgnore.getFileContent()
|
|
28124
|
+
});
|
|
28125
|
+
}
|
|
28126
|
+
static async fromFile({ outputRoot = process.cwd(), validate = true }) {
|
|
28127
|
+
const paths = this.getSettablePaths();
|
|
28128
|
+
const fileContent = await readFileContent(join(outputRoot, paths.relativeDirPath, paths.relativeFilePath));
|
|
28129
|
+
return new BobIgnore({
|
|
28130
|
+
outputRoot,
|
|
28131
|
+
relativeDirPath: paths.relativeDirPath,
|
|
28132
|
+
relativeFilePath: paths.relativeFilePath,
|
|
28133
|
+
fileContent,
|
|
28134
|
+
validate
|
|
28135
|
+
});
|
|
28136
|
+
}
|
|
28137
|
+
static forDeletion({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath }) {
|
|
28138
|
+
return new BobIgnore({
|
|
28139
|
+
outputRoot,
|
|
28140
|
+
relativeDirPath,
|
|
28141
|
+
relativeFilePath,
|
|
28142
|
+
fileContent: "",
|
|
28143
|
+
validate: false
|
|
28144
|
+
});
|
|
28145
|
+
}
|
|
28146
|
+
};
|
|
28147
|
+
//#endregion
|
|
26964
28148
|
//#region src/features/ignore/claudecode-ignore.ts
|
|
26965
28149
|
const DEFAULT_FILE_MODE = "shared";
|
|
26966
28150
|
/**
|
|
@@ -27990,6 +29174,60 @@ var RooIgnore = class RooIgnore extends ToolIgnore {
|
|
|
27990
29174
|
}
|
|
27991
29175
|
};
|
|
27992
29176
|
//#endregion
|
|
29177
|
+
//#region src/features/ignore/tabnine-ignore.ts
|
|
29178
|
+
/**
|
|
29179
|
+
* TabnineIgnore represents the `.tabnineignore` file of Tabnine CLI.
|
|
29180
|
+
*
|
|
29181
|
+
* - File location: project root only (`.tabnineignore`); no user-scope
|
|
29182
|
+
* variant is documented, so the feature is project-only.
|
|
29183
|
+
* - Syntax: the same as `.gitignore`.
|
|
29184
|
+
* - Matching files are excluded from the CLI's file searches and directory
|
|
29185
|
+
* listings while `context.fileFiltering.respectGeminiIgnore` (the default)
|
|
29186
|
+
* is enabled.
|
|
29187
|
+
*
|
|
29188
|
+
* @see https://docs.tabnine.com/main/getting-started/tabnine-cli/features/settings/settings-reference
|
|
29189
|
+
*/
|
|
29190
|
+
var TabnineIgnore = class TabnineIgnore extends ToolIgnore {
|
|
29191
|
+
static getSettablePaths() {
|
|
29192
|
+
return {
|
|
29193
|
+
relativeDirPath: ".",
|
|
29194
|
+
relativeFilePath: TABNINE_IGNORE_FILE_NAME
|
|
29195
|
+
};
|
|
29196
|
+
}
|
|
29197
|
+
toRulesyncIgnore() {
|
|
29198
|
+
return this.toRulesyncIgnoreDefault();
|
|
29199
|
+
}
|
|
29200
|
+
static fromRulesyncIgnore({ outputRoot = process.cwd(), rulesyncIgnore }) {
|
|
29201
|
+
const paths = this.getSettablePaths();
|
|
29202
|
+
return new TabnineIgnore({
|
|
29203
|
+
outputRoot,
|
|
29204
|
+
relativeDirPath: paths.relativeDirPath,
|
|
29205
|
+
relativeFilePath: paths.relativeFilePath,
|
|
29206
|
+
fileContent: rulesyncIgnore.getFileContent()
|
|
29207
|
+
});
|
|
29208
|
+
}
|
|
29209
|
+
static async fromFile({ outputRoot = process.cwd(), validate = true }) {
|
|
29210
|
+
const paths = this.getSettablePaths();
|
|
29211
|
+
const fileContent = await readFileContent(join(outputRoot, paths.relativeDirPath, paths.relativeFilePath));
|
|
29212
|
+
return new TabnineIgnore({
|
|
29213
|
+
outputRoot,
|
|
29214
|
+
relativeDirPath: paths.relativeDirPath,
|
|
29215
|
+
relativeFilePath: paths.relativeFilePath,
|
|
29216
|
+
fileContent,
|
|
29217
|
+
validate
|
|
29218
|
+
});
|
|
29219
|
+
}
|
|
29220
|
+
static forDeletion({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath }) {
|
|
29221
|
+
return new TabnineIgnore({
|
|
29222
|
+
outputRoot,
|
|
29223
|
+
relativeDirPath,
|
|
29224
|
+
relativeFilePath,
|
|
29225
|
+
fileContent: "",
|
|
29226
|
+
validate: false
|
|
29227
|
+
});
|
|
29228
|
+
}
|
|
29229
|
+
};
|
|
29230
|
+
//#endregion
|
|
27993
29231
|
//#region src/features/ignore/vibe-ignore.ts
|
|
27994
29232
|
var VibeIgnore = class VibeIgnore extends ToolIgnore {
|
|
27995
29233
|
static getSettablePaths() {
|
|
@@ -28195,6 +29433,7 @@ const toolIgnoreFactories = /* @__PURE__ */ new Map([
|
|
|
28195
29433
|
["aiassistant", { class: AiassistantIgnore }],
|
|
28196
29434
|
["antigravity-cli", { class: AntigravityCliIgnore }],
|
|
28197
29435
|
["augmentcode", { class: AugmentcodeIgnore }],
|
|
29436
|
+
["bob", { class: BobIgnore }],
|
|
28198
29437
|
["claudecode", { class: ClaudecodeIgnore }],
|
|
28199
29438
|
["claudecode-legacy", { class: ClaudecodeIgnore }],
|
|
28200
29439
|
["cline", { class: ClineIgnore }],
|
|
@@ -28211,6 +29450,7 @@ const toolIgnoreFactories = /* @__PURE__ */ new Map([
|
|
|
28211
29450
|
["roo", { class: RooIgnore }],
|
|
28212
29451
|
["zoocode", { class: RooIgnore }],
|
|
28213
29452
|
["devin", { class: DevinIgnore }],
|
|
29453
|
+
["tabnine", { class: TabnineIgnore }],
|
|
28214
29454
|
["vibe", { class: VibeIgnore }],
|
|
28215
29455
|
["warp", { class: WarpIgnore }],
|
|
28216
29456
|
["zed", { class: ZedIgnore }]
|
|
@@ -28945,6 +30185,343 @@ var AugmentcodeMcp = class AugmentcodeMcp extends ToolMcp {
|
|
|
28945
30185
|
}
|
|
28946
30186
|
};
|
|
28947
30187
|
//#endregion
|
|
30188
|
+
//#region src/features/mcp/mcp-transport.ts
|
|
30189
|
+
/**
|
|
30190
|
+
* A server that names no way to be reached at all. It is what a Kilo bare
|
|
30191
|
+
* `{"enabled": <bool>}` entry imports as — a switch for a server another config
|
|
30192
|
+
* layer defines, which only Kilo's config spells this way. Every other tool's
|
|
30193
|
+
* config defines the servers it lists, so there the entry has no equivalent and
|
|
30194
|
+
* must be skipped rather than written as a server with an empty command.
|
|
30195
|
+
*/
|
|
30196
|
+
function declaresNoTransport(serverConfig) {
|
|
30197
|
+
return serverConfig.type === void 0 && serverConfig.transport === void 0 && serverConfig.command === void 0 && serverConfig.url === void 0 && serverConfig.httpUrl === void 0;
|
|
30198
|
+
}
|
|
30199
|
+
const REMOTE_TRANSPORTS = /* @__PURE__ */ new Set([
|
|
30200
|
+
"sse",
|
|
30201
|
+
"http",
|
|
30202
|
+
"streamable-http",
|
|
30203
|
+
"ws"
|
|
30204
|
+
]);
|
|
30205
|
+
/**
|
|
30206
|
+
* Whether the server speaks over the network rather than over a spawned
|
|
30207
|
+
* process. `httpUrl`, the Claude-specific alias several adapters accept, counts
|
|
30208
|
+
* as much as `url` does, and so does `transport`, the alias `type` has
|
|
30209
|
+
* everywhere in the canonical config.
|
|
30210
|
+
*/
|
|
30211
|
+
function isRemoteMcpServer(serverConfig) {
|
|
30212
|
+
return REMOTE_TRANSPORTS.has(serverConfig.type ?? "") || REMOTE_TRANSPORTS.has(serverConfig.transport ?? "") || serverConfig.url !== void 0 || serverConfig.httpUrl !== void 0;
|
|
30213
|
+
}
|
|
30214
|
+
/** The URL a remote server is reached at, or `undefined` if it states none. */
|
|
30215
|
+
function resolveRemoteMcpUrl(serverConfig) {
|
|
30216
|
+
return serverConfig.url || serverConfig.httpUrl || void 0;
|
|
30217
|
+
}
|
|
30218
|
+
/** The `command` array a `local` server is spawned with, `args` merged in. */
|
|
30219
|
+
function resolveLocalMcpCommand(serverConfig) {
|
|
30220
|
+
const commandArray = [];
|
|
30221
|
+
if (serverConfig.command) {
|
|
30222
|
+
if (Array.isArray(serverConfig.command)) commandArray.push(...serverConfig.command);
|
|
30223
|
+
else commandArray.push(serverConfig.command);
|
|
30224
|
+
}
|
|
30225
|
+
if (serverConfig.args) commandArray.push(...serverConfig.args);
|
|
30226
|
+
return commandArray;
|
|
30227
|
+
}
|
|
30228
|
+
/**
|
|
30229
|
+
* A server that names a transport but carries nothing to reach it — a `type`
|
|
30230
|
+
* with no `command`, an `http` with no `url` — has no form in a config that
|
|
30231
|
+
* spells the transport out. Writing `{type: "local", command: []}` for it would
|
|
30232
|
+
* give the tool a server it cannot start and the importer a file it cannot read
|
|
30233
|
+
* back, so it is skipped out loud instead.
|
|
30234
|
+
*/
|
|
30235
|
+
function warnAndSkipMcpServer({ toolName, serverName, reason, logger }) {
|
|
30236
|
+
logger?.warn(`${toolName} MCP: skipping "${serverName}" because it declares ${reason}.`);
|
|
30237
|
+
return null;
|
|
30238
|
+
}
|
|
30239
|
+
/**
|
|
30240
|
+
* The `tools` map Kilo and OpenCode keep beside `mcp` is keyed `<server>_<tool>`
|
|
30241
|
+
* and reaches servers `mcp` does not list — ones the global config or a
|
|
30242
|
+
* marketplace defines — so entries naming no listed
|
|
30243
|
+
* server are filters that would be dropped by a walk over `mcp` alone, and
|
|
30244
|
+
* deleted from the file on the next generate. They come back as transport-less
|
|
30245
|
+
* servers carrying nothing but the filters.
|
|
30246
|
+
*
|
|
30247
|
+
* The split is at the first `_`, which is where the write side joined them. A
|
|
30248
|
+
* server whose own name contains one is reconstructed under a shorter name, but
|
|
30249
|
+
* the key it is written back as is character-for-character the one that was
|
|
30250
|
+
* read, so Kilo sees exactly what it saw before.
|
|
30251
|
+
*/
|
|
30252
|
+
function orphanMcpToolFiltersToRulesync(servers, tools) {
|
|
30253
|
+
const orphans = {};
|
|
30254
|
+
for (const [toolName, enabled] of Object.entries(tools ?? {})) {
|
|
30255
|
+
if (Object.keys(servers).some((serverName) => toolName.startsWith(`${serverName}_`))) continue;
|
|
30256
|
+
const separator = toolName.indexOf("_");
|
|
30257
|
+
if (separator <= 0 || separator === toolName.length - 1) continue;
|
|
30258
|
+
const serverName = toolName.slice(0, separator);
|
|
30259
|
+
const toolSuffix = toolName.slice(separator + 1);
|
|
30260
|
+
if (!Object.hasOwn(orphans, serverName)) orphans[serverName] = {};
|
|
30261
|
+
const server = orphans[serverName];
|
|
30262
|
+
if (enabled) server.enabledTools = [...server.enabledTools ?? [], toolSuffix];
|
|
30263
|
+
else server.disabledTools = [...server.disabledTools ?? [], toolSuffix];
|
|
30264
|
+
}
|
|
30265
|
+
return orphans;
|
|
30266
|
+
}
|
|
30267
|
+
/**
|
|
30268
|
+
* Split imported servers into the ones every tool can use and the ones only the
|
|
30269
|
+
* importing tool can. A server with no transport is a Kilo/OpenCode idea — a
|
|
30270
|
+
* switch for a server another config layer defines, or a filter for one — and
|
|
30271
|
+
* nothing else knows what to do with it: written to `.mcp.json` and the rest,
|
|
30272
|
+
* it is an entry no tool can start. It goes into the tool-scoped
|
|
30273
|
+
* `{tool}.mcpServers` block instead, which only that tool reads.
|
|
30274
|
+
*/
|
|
30275
|
+
function splitMcpServersByTransport(servers) {
|
|
30276
|
+
const shared = {};
|
|
30277
|
+
const toolOnly = {};
|
|
30278
|
+
for (const [serverName, serverConfig] of Object.entries(servers)) if (declaresNoTransport(serverConfig)) toolOnly[serverName] = serverConfig;
|
|
30279
|
+
else shared[serverName] = serverConfig;
|
|
30280
|
+
return {
|
|
30281
|
+
shared,
|
|
30282
|
+
toolOnly
|
|
30283
|
+
};
|
|
30284
|
+
}
|
|
30285
|
+
//#endregion
|
|
30286
|
+
//#region src/features/mcp/bob-mcp.ts
|
|
30287
|
+
/**
|
|
30288
|
+
* Parse a Bob MCP file, failing closed on malformed JSON or a non-object root
|
|
30289
|
+
* (`null`, an array, a scalar) rather than spreading whatever came back into
|
|
30290
|
+
* the regenerated file.
|
|
30291
|
+
*/
|
|
30292
|
+
function parseBobMcpConfig({ fileContent, relativePath }) {
|
|
30293
|
+
let parsed;
|
|
30294
|
+
try {
|
|
30295
|
+
parsed = JSON.parse(fileContent);
|
|
30296
|
+
} catch (error) {
|
|
30297
|
+
throw new Error(`Failed to parse Bob MCP config at ${relativePath}: ${formatError(error)}`, { cause: error });
|
|
30298
|
+
}
|
|
30299
|
+
if (!isPlainObject$1(parsed)) throw new Error(`Failed to parse Bob MCP config at ${relativePath}: expected a JSON object at the root`);
|
|
30300
|
+
return parsed;
|
|
30301
|
+
}
|
|
30302
|
+
/** The `type` Bob IDE writes for a streamable HTTP server. */
|
|
30303
|
+
const BOB_STREAMABLE_HTTP_TYPE = "streamable-http";
|
|
30304
|
+
/**
|
|
30305
|
+
* The remote transport Bob IDE reads a server as, spelled the way it writes
|
|
30306
|
+
* it: `streamable-http` needs an explicit `type`, while SSE (legacy) is a bare
|
|
30307
|
+
* `url` with no `type`. `http` is the canonical rulesync alias for streamable
|
|
30308
|
+
* HTTP and a bare `url` defaults to it, streamable HTTP being the current MCP
|
|
30309
|
+
* transport. A `ws(s)://` URL or any other stated transport is something Bob
|
|
30310
|
+
* cannot reach, so `undefined` tells the caller to skip the server.
|
|
30311
|
+
* @see https://bob.ibm.com/docs/ide/configuration/mcp/mcp-in-bob
|
|
30312
|
+
*/
|
|
30313
|
+
function asBobRemoteType(stated, url) {
|
|
30314
|
+
if (stated === "sse") return "sse";
|
|
30315
|
+
if (stated === "http" || stated === BOB_STREAMABLE_HTTP_TYPE) return BOB_STREAMABLE_HTTP_TYPE;
|
|
30316
|
+
if (stated === void 0) return /^wss?:\/\//i.test(url) ? void 0 : BOB_STREAMABLE_HTTP_TYPE;
|
|
30317
|
+
}
|
|
30318
|
+
/**
|
|
30319
|
+
* Convert the canonical server map to the shape Bob IDE documents for
|
|
30320
|
+
* `.bob/mcp.json`: a stdio server carries `command` (plus `args`), a
|
|
30321
|
+
* streamable HTTP server carries `type: "streamable-http"` and `url`, and an
|
|
30322
|
+
* SSE server carries a bare `url`. The canonical `transport` alias and the
|
|
30323
|
+
* Claude-style `httpUrl` alias are folded into `type`/`url`; `env`, `cwd`,
|
|
30324
|
+
* `headers`, `timeout`, `alwaysAllow` and `disabled` pass through unchanged,
|
|
30325
|
+
* as Bob documents all of them.
|
|
30326
|
+
*
|
|
30327
|
+
* Bob Shell documents the same file with an `httpURL` key instead of
|
|
30328
|
+
* `type` + `url` for streamable HTTP. rulesync writes the IDE spelling (the two
|
|
30329
|
+
* products share the project file, and the IDE is the one whose global file
|
|
30330
|
+
* rulesync targets) and accepts the Shell spelling on import.
|
|
30331
|
+
*
|
|
30332
|
+
* A server Bob cannot start or reach — no transport at all, a remote transport
|
|
30333
|
+
* without a URL, a WebSocket URL, or a stdio entry without a command — is
|
|
30334
|
+
* skipped with a warning rather than written in a broken form.
|
|
30335
|
+
* @see https://bob.ibm.com/docs/ide/configuration/mcp/mcp-in-bob
|
|
30336
|
+
* @see https://bob.ibm.com/docs/shell/configuration/mcp/mcp-bobshell
|
|
30337
|
+
*/
|
|
30338
|
+
function convertToBobFormat(mcpServers, logger) {
|
|
30339
|
+
const result = {};
|
|
30340
|
+
for (const [serverName, serverConfig] of Object.entries(mcpServers)) {
|
|
30341
|
+
if (PROTOTYPE_POLLUTION_KEYS.has(serverName) || !isRecord$1(serverConfig)) continue;
|
|
30342
|
+
if (declaresNoTransport(serverConfig)) {
|
|
30343
|
+
warnAndSkipMcpServer({
|
|
30344
|
+
toolName: "Bob",
|
|
30345
|
+
serverName,
|
|
30346
|
+
reason: "no transport",
|
|
30347
|
+
logger
|
|
30348
|
+
});
|
|
30349
|
+
continue;
|
|
30350
|
+
}
|
|
30351
|
+
const { type, transport, url: _url, httpUrl: _httpUrl, command: _command, args: _args, ...rest } = serverConfig;
|
|
30352
|
+
const converted = {};
|
|
30353
|
+
if (isRemoteMcpServer(serverConfig)) {
|
|
30354
|
+
const url = resolveRemoteMcpUrl(serverConfig);
|
|
30355
|
+
if (!url) {
|
|
30356
|
+
warnAndSkipMcpServer({
|
|
30357
|
+
toolName: "Bob",
|
|
30358
|
+
serverName,
|
|
30359
|
+
reason: "a remote transport without a url",
|
|
30360
|
+
logger
|
|
30361
|
+
});
|
|
30362
|
+
continue;
|
|
30363
|
+
}
|
|
30364
|
+
const stated = type ?? transport;
|
|
30365
|
+
const remoteType = asBobRemoteType(stated, url);
|
|
30366
|
+
if (remoteType === void 0) {
|
|
30367
|
+
warnAndSkipMcpServer({
|
|
30368
|
+
toolName: "Bob",
|
|
30369
|
+
serverName,
|
|
30370
|
+
reason: stated === void 0 ? "a WebSocket url, which Bob's remote transports (streamable-http and sse) cannot reach" : `the "${stated}" transport, which Bob does not offer for remote servers (only streamable-http and sse)`,
|
|
30371
|
+
logger
|
|
30372
|
+
});
|
|
30373
|
+
continue;
|
|
30374
|
+
}
|
|
30375
|
+
if (remoteType === BOB_STREAMABLE_HTTP_TYPE) converted.type = BOB_STREAMABLE_HTTP_TYPE;
|
|
30376
|
+
converted.url = url;
|
|
30377
|
+
} else {
|
|
30378
|
+
const [command, ...args] = resolveLocalMcpCommand(serverConfig);
|
|
30379
|
+
if (!command) {
|
|
30380
|
+
warnAndSkipMcpServer({
|
|
30381
|
+
toolName: "Bob",
|
|
30382
|
+
serverName,
|
|
30383
|
+
reason: "a stdio transport without a command",
|
|
30384
|
+
logger
|
|
30385
|
+
});
|
|
30386
|
+
continue;
|
|
30387
|
+
}
|
|
30388
|
+
converted.command = command;
|
|
30389
|
+
if (args.length > 0) converted.args = args;
|
|
30390
|
+
}
|
|
30391
|
+
for (const [key, value] of Object.entries(rest)) {
|
|
30392
|
+
if (PROTOTYPE_POLLUTION_KEYS.has(key)) continue;
|
|
30393
|
+
converted[key] = (key === "env" || key === "headers") && isRecord$1(value) ? omitPrototypePollutionKeys(value) : value;
|
|
30394
|
+
}
|
|
30395
|
+
result[serverName] = converted;
|
|
30396
|
+
}
|
|
30397
|
+
return result;
|
|
30398
|
+
}
|
|
30399
|
+
/**
|
|
30400
|
+
* Convert Bob's server map back to the canonical shape. The IDE spelling
|
|
30401
|
+
* (`type: "streamable-http"` + `url`) is already canonical and passes through;
|
|
30402
|
+
* the Bob Shell spelling `httpURL` becomes `url` with `type: "http"`; a bare
|
|
30403
|
+
* `url` is an SSE server in Bob, so it gains `type: "sse"` to keep that reading
|
|
30404
|
+
* on the next generate.
|
|
30405
|
+
*/
|
|
30406
|
+
function convertFromBobFormat(mcpServers) {
|
|
30407
|
+
if (!isMcpServers(mcpServers)) return {};
|
|
30408
|
+
const result = {};
|
|
30409
|
+
for (const [serverName, serverConfig] of Object.entries(mcpServers)) {
|
|
30410
|
+
if (PROTOTYPE_POLLUTION_KEYS.has(serverName) || !isRecord$1(serverConfig)) continue;
|
|
30411
|
+
const converted = {};
|
|
30412
|
+
let httpURL;
|
|
30413
|
+
for (const [key, value] of Object.entries(serverConfig)) {
|
|
30414
|
+
if (PROTOTYPE_POLLUTION_KEYS.has(key)) continue;
|
|
30415
|
+
if (key === "httpURL") {
|
|
30416
|
+
if (typeof value === "string") httpURL = value;
|
|
30417
|
+
continue;
|
|
30418
|
+
}
|
|
30419
|
+
converted[key] = value;
|
|
30420
|
+
}
|
|
30421
|
+
if (httpURL !== void 0) {
|
|
30422
|
+
converted.url = httpURL;
|
|
30423
|
+
converted.type = "http";
|
|
30424
|
+
} else if (typeof converted.url === "string" && converted.type === void 0) converted.type = "sse";
|
|
30425
|
+
result[serverName] = converted;
|
|
30426
|
+
}
|
|
30427
|
+
return result;
|
|
30428
|
+
}
|
|
30429
|
+
/**
|
|
30430
|
+
* IBM Bob MCP configuration.
|
|
30431
|
+
*
|
|
30432
|
+
* Bob IDE reads `mcpServers` from `<project>/.bob/mcp.json` (project scope)
|
|
30433
|
+
* and `~/.bob/mcp.json` (user scope); the project entry wins when both define
|
|
30434
|
+
* the same server name. Both files are dedicated to MCP. The project one is
|
|
30435
|
+
* deletable; the user one is created and edited by Bob IDE's own MCP settings
|
|
30436
|
+
* UI, so `--delete` leaves it in place rather than removing a file the user
|
|
30437
|
+
* did not create through rulesync. (Bob Shell reads its user-scoped servers
|
|
30438
|
+
* from `~/.bob/mcp_settings.json` instead, which rulesync does not write.)
|
|
30439
|
+
*
|
|
30440
|
+
* @see https://bob.ibm.com/docs/ide/configuration/mcp/mcp-in-bob
|
|
30441
|
+
* @see https://bob.ibm.com/docs/shell/configuration/mcp/mcp-bobshell
|
|
30442
|
+
*/
|
|
30443
|
+
var BobMcp = class BobMcp extends ToolMcp {
|
|
30444
|
+
json;
|
|
30445
|
+
constructor(params) {
|
|
30446
|
+
super(params);
|
|
30447
|
+
this.json = this.fileContent === void 0 ? {} : parseBobMcpConfig({
|
|
30448
|
+
fileContent: this.fileContent,
|
|
30449
|
+
relativePath: join(this.relativeDirPath, this.relativeFilePath)
|
|
30450
|
+
});
|
|
30451
|
+
}
|
|
30452
|
+
getJson() {
|
|
30453
|
+
return this.json;
|
|
30454
|
+
}
|
|
30455
|
+
isDeletable() {
|
|
30456
|
+
return !this.global;
|
|
30457
|
+
}
|
|
30458
|
+
static getSettablePaths(_options = {}) {
|
|
30459
|
+
return {
|
|
30460
|
+
relativeDirPath: BOB_DIR,
|
|
30461
|
+
relativeFilePath: BOB_MCP_FILE_NAME
|
|
30462
|
+
};
|
|
30463
|
+
}
|
|
30464
|
+
static async fromFile({ outputRoot = process.cwd(), validate = true, global = false }) {
|
|
30465
|
+
const paths = this.getSettablePaths({ global });
|
|
30466
|
+
const json = parseBobMcpConfig({
|
|
30467
|
+
fileContent: await readFileContentOrNull(join(outputRoot, paths.relativeDirPath, paths.relativeFilePath)) ?? "{\"mcpServers\":{}}",
|
|
30468
|
+
relativePath: join(paths.relativeDirPath, paths.relativeFilePath)
|
|
30469
|
+
});
|
|
30470
|
+
const newJson = {
|
|
30471
|
+
...json,
|
|
30472
|
+
mcpServers: json.mcpServers ?? {}
|
|
30473
|
+
};
|
|
30474
|
+
return new BobMcp({
|
|
30475
|
+
outputRoot,
|
|
30476
|
+
relativeDirPath: paths.relativeDirPath,
|
|
30477
|
+
relativeFilePath: paths.relativeFilePath,
|
|
30478
|
+
fileContent: JSON.stringify(newJson, null, 2),
|
|
30479
|
+
validate,
|
|
30480
|
+
global
|
|
30481
|
+
});
|
|
30482
|
+
}
|
|
30483
|
+
static async fromRulesyncMcp({ outputRoot = process.cwd(), rulesyncMcp, validate = true, global = false, logger }) {
|
|
30484
|
+
const paths = this.getSettablePaths({ global });
|
|
30485
|
+
const json = parseBobMcpConfig({
|
|
30486
|
+
fileContent: await readFileContentOrNull(join(outputRoot, paths.relativeDirPath, paths.relativeFilePath)) ?? JSON.stringify({ mcpServers: {} }, null, 2),
|
|
30487
|
+
relativePath: join(paths.relativeDirPath, paths.relativeFilePath)
|
|
30488
|
+
});
|
|
30489
|
+
const mcpServers = convertToBobFormat(rulesyncMcp.getMcpServers(), logger);
|
|
30490
|
+
const bobConfig = {
|
|
30491
|
+
...json,
|
|
30492
|
+
mcpServers
|
|
30493
|
+
};
|
|
30494
|
+
return new BobMcp({
|
|
30495
|
+
outputRoot,
|
|
30496
|
+
relativeDirPath: paths.relativeDirPath,
|
|
30497
|
+
relativeFilePath: paths.relativeFilePath,
|
|
30498
|
+
fileContent: JSON.stringify(bobConfig, null, 2),
|
|
30499
|
+
validate,
|
|
30500
|
+
global
|
|
30501
|
+
});
|
|
30502
|
+
}
|
|
30503
|
+
toRulesyncMcp() {
|
|
30504
|
+
const mcpServers = convertFromBobFormat(this.json.mcpServers);
|
|
30505
|
+
return this.toRulesyncMcpDefault({ fileContent: JSON.stringify({ mcpServers }, null, 2) });
|
|
30506
|
+
}
|
|
30507
|
+
validate() {
|
|
30508
|
+
return {
|
|
30509
|
+
success: true,
|
|
30510
|
+
error: null
|
|
30511
|
+
};
|
|
30512
|
+
}
|
|
30513
|
+
static forDeletion({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath, global = false }) {
|
|
30514
|
+
return new BobMcp({
|
|
30515
|
+
outputRoot,
|
|
30516
|
+
relativeDirPath,
|
|
30517
|
+
relativeFilePath,
|
|
30518
|
+
fileContent: "{}",
|
|
30519
|
+
validate: false,
|
|
30520
|
+
global
|
|
30521
|
+
});
|
|
30522
|
+
}
|
|
30523
|
+
};
|
|
30524
|
+
//#endregion
|
|
28948
30525
|
//#region src/features/mcp/claudecode-mcp.ts
|
|
28949
30526
|
var ClaudecodeMcp = class ClaudecodeMcp extends ToolMcp {
|
|
28950
30527
|
json;
|
|
@@ -29652,104 +31229,6 @@ var CopilotMcp = class CopilotMcp extends ToolMcp {
|
|
|
29652
31229
|
}
|
|
29653
31230
|
};
|
|
29654
31231
|
//#endregion
|
|
29655
|
-
//#region src/features/mcp/mcp-transport.ts
|
|
29656
|
-
/**
|
|
29657
|
-
* A server that names no way to be reached at all. It is what a Kilo bare
|
|
29658
|
-
* `{"enabled": <bool>}` entry imports as — a switch for a server another config
|
|
29659
|
-
* layer defines, which only Kilo's config spells this way. Every other tool's
|
|
29660
|
-
* config defines the servers it lists, so there the entry has no equivalent and
|
|
29661
|
-
* must be skipped rather than written as a server with an empty command.
|
|
29662
|
-
*/
|
|
29663
|
-
function declaresNoTransport(serverConfig) {
|
|
29664
|
-
return serverConfig.type === void 0 && serverConfig.transport === void 0 && serverConfig.command === void 0 && serverConfig.url === void 0 && serverConfig.httpUrl === void 0;
|
|
29665
|
-
}
|
|
29666
|
-
const REMOTE_TRANSPORTS = /* @__PURE__ */ new Set([
|
|
29667
|
-
"sse",
|
|
29668
|
-
"http",
|
|
29669
|
-
"streamable-http",
|
|
29670
|
-
"ws"
|
|
29671
|
-
]);
|
|
29672
|
-
/**
|
|
29673
|
-
* Whether the server speaks over the network rather than over a spawned
|
|
29674
|
-
* process. `httpUrl`, the Claude-specific alias several adapters accept, counts
|
|
29675
|
-
* as much as `url` does, and so does `transport`, the alias `type` has
|
|
29676
|
-
* everywhere in the canonical config.
|
|
29677
|
-
*/
|
|
29678
|
-
function isRemoteMcpServer(serverConfig) {
|
|
29679
|
-
return REMOTE_TRANSPORTS.has(serverConfig.type ?? "") || REMOTE_TRANSPORTS.has(serverConfig.transport ?? "") || serverConfig.url !== void 0 || serverConfig.httpUrl !== void 0;
|
|
29680
|
-
}
|
|
29681
|
-
/** The URL a remote server is reached at, or `undefined` if it states none. */
|
|
29682
|
-
function resolveRemoteMcpUrl(serverConfig) {
|
|
29683
|
-
return serverConfig.url || serverConfig.httpUrl || void 0;
|
|
29684
|
-
}
|
|
29685
|
-
/** The `command` array a `local` server is spawned with, `args` merged in. */
|
|
29686
|
-
function resolveLocalMcpCommand(serverConfig) {
|
|
29687
|
-
const commandArray = [];
|
|
29688
|
-
if (serverConfig.command) {
|
|
29689
|
-
if (Array.isArray(serverConfig.command)) commandArray.push(...serverConfig.command);
|
|
29690
|
-
else commandArray.push(serverConfig.command);
|
|
29691
|
-
}
|
|
29692
|
-
if (serverConfig.args) commandArray.push(...serverConfig.args);
|
|
29693
|
-
return commandArray;
|
|
29694
|
-
}
|
|
29695
|
-
/**
|
|
29696
|
-
* A server that names a transport but carries nothing to reach it — a `type`
|
|
29697
|
-
* with no `command`, an `http` with no `url` — has no form in a config that
|
|
29698
|
-
* spells the transport out. Writing `{type: "local", command: []}` for it would
|
|
29699
|
-
* give the tool a server it cannot start and the importer a file it cannot read
|
|
29700
|
-
* back, so it is skipped out loud instead.
|
|
29701
|
-
*/
|
|
29702
|
-
function warnAndSkipMcpServer({ toolName, serverName, reason, logger }) {
|
|
29703
|
-
logger?.warn(`${toolName} MCP: skipping "${serverName}" because it declares ${reason}.`);
|
|
29704
|
-
return null;
|
|
29705
|
-
}
|
|
29706
|
-
/**
|
|
29707
|
-
* The `tools` map Kilo and OpenCode keep beside `mcp` is keyed `<server>_<tool>`
|
|
29708
|
-
* and reaches servers `mcp` does not list — ones the global config or a
|
|
29709
|
-
* marketplace defines — so entries naming no listed
|
|
29710
|
-
* server are filters that would be dropped by a walk over `mcp` alone, and
|
|
29711
|
-
* deleted from the file on the next generate. They come back as transport-less
|
|
29712
|
-
* servers carrying nothing but the filters.
|
|
29713
|
-
*
|
|
29714
|
-
* The split is at the first `_`, which is where the write side joined them. A
|
|
29715
|
-
* server whose own name contains one is reconstructed under a shorter name, but
|
|
29716
|
-
* the key it is written back as is character-for-character the one that was
|
|
29717
|
-
* read, so Kilo sees exactly what it saw before.
|
|
29718
|
-
*/
|
|
29719
|
-
function orphanMcpToolFiltersToRulesync(servers, tools) {
|
|
29720
|
-
const orphans = {};
|
|
29721
|
-
for (const [toolName, enabled] of Object.entries(tools ?? {})) {
|
|
29722
|
-
if (Object.keys(servers).some((serverName) => toolName.startsWith(`${serverName}_`))) continue;
|
|
29723
|
-
const separator = toolName.indexOf("_");
|
|
29724
|
-
if (separator <= 0 || separator === toolName.length - 1) continue;
|
|
29725
|
-
const serverName = toolName.slice(0, separator);
|
|
29726
|
-
const toolSuffix = toolName.slice(separator + 1);
|
|
29727
|
-
if (!Object.hasOwn(orphans, serverName)) orphans[serverName] = {};
|
|
29728
|
-
const server = orphans[serverName];
|
|
29729
|
-
if (enabled) server.enabledTools = [...server.enabledTools ?? [], toolSuffix];
|
|
29730
|
-
else server.disabledTools = [...server.disabledTools ?? [], toolSuffix];
|
|
29731
|
-
}
|
|
29732
|
-
return orphans;
|
|
29733
|
-
}
|
|
29734
|
-
/**
|
|
29735
|
-
* Split imported servers into the ones every tool can use and the ones only the
|
|
29736
|
-
* importing tool can. A server with no transport is a Kilo/OpenCode idea — a
|
|
29737
|
-
* switch for a server another config layer defines, or a filter for one — and
|
|
29738
|
-
* nothing else knows what to do with it: written to `.mcp.json` and the rest,
|
|
29739
|
-
* it is an entry no tool can start. It goes into the tool-scoped
|
|
29740
|
-
* `{tool}.mcpServers` block instead, which only that tool reads.
|
|
29741
|
-
*/
|
|
29742
|
-
function splitMcpServersByTransport(servers) {
|
|
29743
|
-
const shared = {};
|
|
29744
|
-
const toolOnly = {};
|
|
29745
|
-
for (const [serverName, serverConfig] of Object.entries(servers)) if (declaresNoTransport(serverConfig)) toolOnly[serverName] = serverConfig;
|
|
29746
|
-
else shared[serverName] = serverConfig;
|
|
29747
|
-
return {
|
|
29748
|
-
shared,
|
|
29749
|
-
toolOnly
|
|
29750
|
-
};
|
|
29751
|
-
}
|
|
29752
|
-
//#endregion
|
|
29753
31232
|
//#region src/features/mcp/copilotcli-mcp.ts
|
|
29754
31233
|
/**
|
|
29755
31234
|
* Reached over WebSocket, which Copilot CLI has no transport for. The url is
|
|
@@ -30018,6 +31497,222 @@ var CopilotcliMcp = class CopilotcliMcp extends ToolMcp {
|
|
|
30018
31497
|
}
|
|
30019
31498
|
};
|
|
30020
31499
|
//#endregion
|
|
31500
|
+
//#region src/features/mcp/cortexcode-mcp.ts
|
|
31501
|
+
const CORTEXCODE_GLOBAL_ONLY_MESSAGE = "Snowflake Cortex Code MCP is global-only; use --global to sync ~/.snowflake/cortex/mcp.json";
|
|
31502
|
+
/**
|
|
31503
|
+
* Parse a Cortex Code MCP file, failing closed on malformed JSON or a
|
|
31504
|
+
* non-object root (`null`, an array, a scalar) rather than spreading whatever
|
|
31505
|
+
* came back into the regenerated file.
|
|
31506
|
+
*/
|
|
31507
|
+
function parseCortexcodeMcpConfig({ fileContent, relativePath }) {
|
|
31508
|
+
let parsed;
|
|
31509
|
+
try {
|
|
31510
|
+
parsed = JSON.parse(fileContent);
|
|
31511
|
+
} catch (error) {
|
|
31512
|
+
throw new Error(`Failed to parse Cortex Code MCP config at ${relativePath}: ${formatError(error)}`, { cause: error });
|
|
31513
|
+
}
|
|
31514
|
+
if (!isPlainObject$1(parsed)) throw new Error(`Failed to parse Cortex Code MCP config at ${relativePath}: expected a JSON object at the root`);
|
|
31515
|
+
return parsed;
|
|
31516
|
+
}
|
|
31517
|
+
/**
|
|
31518
|
+
* The remote transport Cortex Code reads a server as. Cortex Code documents
|
|
31519
|
+
* exactly three transports — `stdio`, `http` and `sse` — each spelled out in
|
|
31520
|
+
* an explicit `type` key. `http` is also the canonical rulesync spelling, and
|
|
31521
|
+
* a bare `url` defaults to it, streamable HTTP being the current MCP
|
|
31522
|
+
* transport. `streamable-http` is folded into `http`. A `ws(s)://` URL or any
|
|
31523
|
+
* other stated transport is something Cortex Code cannot reach, so
|
|
31524
|
+
* `undefined` tells the caller to skip the server.
|
|
31525
|
+
* @see https://docs.snowflake.com/en/user-guide/cortex-code/extensibility#transport-types
|
|
31526
|
+
*/
|
|
31527
|
+
function asCortexcodeRemoteType(stated, url) {
|
|
31528
|
+
if (stated === "sse") return "sse";
|
|
31529
|
+
if (stated === "http" || stated === "streamable-http") return "http";
|
|
31530
|
+
if (stated === void 0) return /^wss?:\/\//i.test(url) ? void 0 : "http";
|
|
31531
|
+
}
|
|
31532
|
+
/**
|
|
31533
|
+
* Convert the canonical server map to the shape Cortex Code documents for
|
|
31534
|
+
* `~/.snowflake/cortex/mcp.json`: every server carries an explicit `type`
|
|
31535
|
+
* (`stdio`, `http` or `sse`); a stdio server carries `command` (plus `args`)
|
|
31536
|
+
* and a remote server carries `url`. The canonical `transport` alias and the
|
|
31537
|
+
* Claude-style `httpUrl` alias are folded into `type`/`url`; `env`, `headers`,
|
|
31538
|
+
* `oauth` and `timeout` pass through, as Cortex Code documents all of them.
|
|
31539
|
+
*
|
|
31540
|
+
* A server Cortex Code cannot start or reach — no transport at all, a remote
|
|
31541
|
+
* transport without a URL, a WebSocket URL, or a stdio entry without a
|
|
31542
|
+
* command — is skipped with a warning rather than written in a broken form.
|
|
31543
|
+
* @see https://docs.snowflake.com/en/user-guide/cortex-code/extensibility#mcp-configuration
|
|
31544
|
+
*/
|
|
31545
|
+
function convertToCortexcodeFormat(mcpServers, logger) {
|
|
31546
|
+
const result = {};
|
|
31547
|
+
for (const [serverName, serverConfig] of Object.entries(mcpServers)) {
|
|
31548
|
+
if (PROTOTYPE_POLLUTION_KEYS.has(serverName) || !isRecord$1(serverConfig)) continue;
|
|
31549
|
+
if (declaresNoTransport(serverConfig)) {
|
|
31550
|
+
warnAndSkipMcpServer({
|
|
31551
|
+
toolName: "Cortex Code",
|
|
31552
|
+
serverName,
|
|
31553
|
+
reason: "no transport",
|
|
31554
|
+
logger
|
|
31555
|
+
});
|
|
31556
|
+
continue;
|
|
31557
|
+
}
|
|
31558
|
+
const { type, transport, url: _url, httpUrl: _httpUrl, command: _command, args: _args, ...rest } = serverConfig;
|
|
31559
|
+
const converted = {};
|
|
31560
|
+
if (isRemoteMcpServer(serverConfig)) {
|
|
31561
|
+
const url = resolveRemoteMcpUrl(serverConfig);
|
|
31562
|
+
if (!url) {
|
|
31563
|
+
warnAndSkipMcpServer({
|
|
31564
|
+
toolName: "Cortex Code",
|
|
31565
|
+
serverName,
|
|
31566
|
+
reason: "a remote transport without a url",
|
|
31567
|
+
logger
|
|
31568
|
+
});
|
|
31569
|
+
continue;
|
|
31570
|
+
}
|
|
31571
|
+
const stated = type ?? transport;
|
|
31572
|
+
const remoteType = asCortexcodeRemoteType(stated, url);
|
|
31573
|
+
if (remoteType === void 0) {
|
|
31574
|
+
warnAndSkipMcpServer({
|
|
31575
|
+
toolName: "Cortex Code",
|
|
31576
|
+
serverName,
|
|
31577
|
+
reason: stated === void 0 ? "a WebSocket url, which Cortex Code's remote transports (http and sse) cannot reach" : `the "${stated}" transport, which Cortex Code does not offer for remote servers (only http and sse)`,
|
|
31578
|
+
logger
|
|
31579
|
+
});
|
|
31580
|
+
continue;
|
|
31581
|
+
}
|
|
31582
|
+
converted.type = remoteType;
|
|
31583
|
+
converted.url = url;
|
|
31584
|
+
} else {
|
|
31585
|
+
const [command, ...args] = resolveLocalMcpCommand(serverConfig);
|
|
31586
|
+
if (!command) {
|
|
31587
|
+
warnAndSkipMcpServer({
|
|
31588
|
+
toolName: "Cortex Code",
|
|
31589
|
+
serverName,
|
|
31590
|
+
reason: "a stdio transport without a command",
|
|
31591
|
+
logger
|
|
31592
|
+
});
|
|
31593
|
+
continue;
|
|
31594
|
+
}
|
|
31595
|
+
converted.type = "stdio";
|
|
31596
|
+
converted.command = command;
|
|
31597
|
+
if (args.length > 0) converted.args = args;
|
|
31598
|
+
}
|
|
31599
|
+
for (const [key, value] of Object.entries(rest)) {
|
|
31600
|
+
if (PROTOTYPE_POLLUTION_KEYS.has(key)) continue;
|
|
31601
|
+
converted[key] = (key === "env" || key === "headers") && isRecord$1(value) ? omitPrototypePollutionKeys(value) : value;
|
|
31602
|
+
}
|
|
31603
|
+
result[serverName] = converted;
|
|
31604
|
+
}
|
|
31605
|
+
return result;
|
|
31606
|
+
}
|
|
31607
|
+
/**
|
|
31608
|
+
* Convert Cortex Code's server map back to the canonical shape. The
|
|
31609
|
+
* documented spelling (`type` + `command`/`url`) is already canonical, so
|
|
31610
|
+
* entries pass through with only prototype-pollution keys dropped.
|
|
31611
|
+
*/
|
|
31612
|
+
function convertFromCortexcodeFormat(mcpServers) {
|
|
31613
|
+
if (!isMcpServers(mcpServers)) return {};
|
|
31614
|
+
const result = {};
|
|
31615
|
+
for (const [serverName, serverConfig] of Object.entries(mcpServers)) {
|
|
31616
|
+
if (PROTOTYPE_POLLUTION_KEYS.has(serverName) || !isRecord$1(serverConfig)) continue;
|
|
31617
|
+
result[serverName] = omitPrototypePollutionKeys(serverConfig);
|
|
31618
|
+
}
|
|
31619
|
+
return result;
|
|
31620
|
+
}
|
|
31621
|
+
/**
|
|
31622
|
+
* Snowflake Cortex Code MCP configuration.
|
|
31623
|
+
*
|
|
31624
|
+
* Cortex Code reads `mcpServers` only from the single user-scoped file
|
|
31625
|
+
* `~/.snowflake/cortex/mcp.json`; no project-scoped MCP location is
|
|
31626
|
+
* documented, so the target is global-only. The file is created and edited by
|
|
31627
|
+
* `cortex mcp add` as well, so `--delete` leaves it in place rather than
|
|
31628
|
+
* removing a file the user did not create through rulesync.
|
|
31629
|
+
*
|
|
31630
|
+
* @see https://docs.snowflake.com/en/user-guide/cortex-code/extensibility#mcp-configuration
|
|
31631
|
+
*/
|
|
31632
|
+
var CortexcodeMcp = class CortexcodeMcp extends ToolMcp {
|
|
31633
|
+
json;
|
|
31634
|
+
constructor(params) {
|
|
31635
|
+
super(params);
|
|
31636
|
+
this.json = this.fileContent === void 0 ? {} : parseCortexcodeMcpConfig({
|
|
31637
|
+
fileContent: this.fileContent,
|
|
31638
|
+
relativePath: join(this.relativeDirPath, this.relativeFilePath)
|
|
31639
|
+
});
|
|
31640
|
+
}
|
|
31641
|
+
getJson() {
|
|
31642
|
+
return this.json;
|
|
31643
|
+
}
|
|
31644
|
+
isDeletable() {
|
|
31645
|
+
return false;
|
|
31646
|
+
}
|
|
31647
|
+
static getSettablePaths(_options = {}) {
|
|
31648
|
+
return {
|
|
31649
|
+
relativeDirPath: CORTEXCODE_GLOBAL_DIR_PATH,
|
|
31650
|
+
relativeFilePath: CORTEXCODE_MCP_FILE_NAME
|
|
31651
|
+
};
|
|
31652
|
+
}
|
|
31653
|
+
static async fromFile({ outputRoot = process.cwd(), validate = true, global = false }) {
|
|
31654
|
+
if (!global) throw new Error(CORTEXCODE_GLOBAL_ONLY_MESSAGE);
|
|
31655
|
+
const paths = this.getSettablePaths({ global });
|
|
31656
|
+
const json = parseCortexcodeMcpConfig({
|
|
31657
|
+
fileContent: await readFileContentOrNull(join(outputRoot, paths.relativeDirPath, paths.relativeFilePath)) ?? "{\"mcpServers\":{}}",
|
|
31658
|
+
relativePath: join(paths.relativeDirPath, paths.relativeFilePath)
|
|
31659
|
+
});
|
|
31660
|
+
const newJson = {
|
|
31661
|
+
...json,
|
|
31662
|
+
mcpServers: json.mcpServers ?? {}
|
|
31663
|
+
};
|
|
31664
|
+
return new CortexcodeMcp({
|
|
31665
|
+
outputRoot,
|
|
31666
|
+
relativeDirPath: paths.relativeDirPath,
|
|
31667
|
+
relativeFilePath: paths.relativeFilePath,
|
|
31668
|
+
fileContent: JSON.stringify(newJson, null, 2),
|
|
31669
|
+
validate,
|
|
31670
|
+
global
|
|
31671
|
+
});
|
|
31672
|
+
}
|
|
31673
|
+
static async fromRulesyncMcp({ outputRoot = process.cwd(), rulesyncMcp, validate = true, global = false, logger }) {
|
|
31674
|
+
if (!global) throw new Error(CORTEXCODE_GLOBAL_ONLY_MESSAGE);
|
|
31675
|
+
const paths = this.getSettablePaths({ global });
|
|
31676
|
+
const json = parseCortexcodeMcpConfig({
|
|
31677
|
+
fileContent: await readFileContentOrNull(join(outputRoot, paths.relativeDirPath, paths.relativeFilePath)) ?? JSON.stringify({ mcpServers: {} }, null, 2),
|
|
31678
|
+
relativePath: join(paths.relativeDirPath, paths.relativeFilePath)
|
|
31679
|
+
});
|
|
31680
|
+
const mcpServers = convertToCortexcodeFormat(rulesyncMcp.getMcpServers(), logger);
|
|
31681
|
+
const cortexcodeConfig = {
|
|
31682
|
+
...json,
|
|
31683
|
+
mcpServers
|
|
31684
|
+
};
|
|
31685
|
+
return new CortexcodeMcp({
|
|
31686
|
+
outputRoot,
|
|
31687
|
+
relativeDirPath: paths.relativeDirPath,
|
|
31688
|
+
relativeFilePath: paths.relativeFilePath,
|
|
31689
|
+
fileContent: JSON.stringify(cortexcodeConfig, null, 2),
|
|
31690
|
+
validate,
|
|
31691
|
+
global
|
|
31692
|
+
});
|
|
31693
|
+
}
|
|
31694
|
+
toRulesyncMcp() {
|
|
31695
|
+
const mcpServers = convertFromCortexcodeFormat(this.json.mcpServers);
|
|
31696
|
+
return this.toRulesyncMcpDefault({ fileContent: JSON.stringify({ mcpServers }, null, 2) });
|
|
31697
|
+
}
|
|
31698
|
+
validate() {
|
|
31699
|
+
return {
|
|
31700
|
+
success: true,
|
|
31701
|
+
error: null
|
|
31702
|
+
};
|
|
31703
|
+
}
|
|
31704
|
+
static forDeletion({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath, global = false }) {
|
|
31705
|
+
return new CortexcodeMcp({
|
|
31706
|
+
outputRoot,
|
|
31707
|
+
relativeDirPath,
|
|
31708
|
+
relativeFilePath,
|
|
31709
|
+
fileContent: "{}",
|
|
31710
|
+
validate: false,
|
|
31711
|
+
global
|
|
31712
|
+
});
|
|
31713
|
+
}
|
|
31714
|
+
};
|
|
31715
|
+
//#endregion
|
|
30021
31716
|
//#region src/features/mcp/mcp-env-var-format.ts
|
|
30022
31717
|
/**
|
|
30023
31718
|
* Canonical rulesync env var reference pattern: `${VAR}` (but not `${env:VAR}`,
|
|
@@ -33979,6 +35674,211 @@ var RovodevMcp = class RovodevMcp extends ToolMcp {
|
|
|
33979
35674
|
}
|
|
33980
35675
|
};
|
|
33981
35676
|
//#endregion
|
|
35677
|
+
//#region src/features/mcp/tabnine-mcp.ts
|
|
35678
|
+
/**
|
|
35679
|
+
* The remote `type` Tabnine CLI reads a server as. Tabnine documents `"http"`
|
|
35680
|
+
* (streamable HTTP, the default for a bare `url`) and `"sse"`; `streamable-http`
|
|
35681
|
+
* is the canonical alias for the former. A `ws(s)://` URL or any other stated
|
|
35682
|
+
* transport is something Tabnine cannot reach, so `undefined` tells the caller
|
|
35683
|
+
* to skip the server. `null` means "omit `type`": Tabnine then tries
|
|
35684
|
+
* streamable HTTP first and falls back to SSE on its own.
|
|
35685
|
+
* @see https://docs.tabnine.com/main/getting-started/tabnine-agent/mcp-intro-and-setup/mcp-server-config
|
|
35686
|
+
*/
|
|
35687
|
+
function asTabnineRemoteType(stated, url) {
|
|
35688
|
+
if (stated === "sse") return "sse";
|
|
35689
|
+
if (stated === "http" || stated === "streamable-http") return "http";
|
|
35690
|
+
if (stated === void 0) return /^wss?:\/\//i.test(url) ? void 0 : null;
|
|
35691
|
+
}
|
|
35692
|
+
/**
|
|
35693
|
+
* Convert the canonical server map to the shape Tabnine CLI documents for the
|
|
35694
|
+
* `mcpServers` block of `settings.json`: a stdio server carries `command` (plus
|
|
35695
|
+
* `args`), a remote one carries `url` and an optional `type` (`"http"` or
|
|
35696
|
+
* `"sse"`). The canonical `transport` alias and the Claude-style `httpUrl`
|
|
35697
|
+
* alias are folded into `type`/`url`; `enabledTools`/`disabledTools` become
|
|
35698
|
+
* Tabnine's `includeTools`/`excludeTools`; `env`, `cwd`, `headers`, `timeout`
|
|
35699
|
+
* and `trust` pass through as documented.
|
|
35700
|
+
*
|
|
35701
|
+
* A server Tabnine cannot start or reach — no transport at all, a remote
|
|
35702
|
+
* transport without a URL, a WebSocket URL, or a stdio entry without a command
|
|
35703
|
+
* — is skipped with a warning rather than written in a broken form.
|
|
35704
|
+
*/
|
|
35705
|
+
function convertToTabnineFormat(mcpServers, logger) {
|
|
35706
|
+
const result = {};
|
|
35707
|
+
for (const [serverName, serverConfig] of Object.entries(mcpServers)) {
|
|
35708
|
+
if (PROTOTYPE_POLLUTION_KEYS.has(serverName) || !isRecord$1(serverConfig)) continue;
|
|
35709
|
+
if (declaresNoTransport(serverConfig)) {
|
|
35710
|
+
warnAndSkipMcpServer({
|
|
35711
|
+
toolName: "Tabnine CLI",
|
|
35712
|
+
serverName,
|
|
35713
|
+
reason: "no transport",
|
|
35714
|
+
logger
|
|
35715
|
+
});
|
|
35716
|
+
continue;
|
|
35717
|
+
}
|
|
35718
|
+
const { type, transport, url: _url, httpUrl: _httpUrl, command: _command, args: _args, enabledTools, disabledTools, ...rest } = serverConfig;
|
|
35719
|
+
const converted = {};
|
|
35720
|
+
if (isRemoteMcpServer(serverConfig)) {
|
|
35721
|
+
const url = resolveRemoteMcpUrl(serverConfig);
|
|
35722
|
+
if (!url) {
|
|
35723
|
+
warnAndSkipMcpServer({
|
|
35724
|
+
toolName: "Tabnine CLI",
|
|
35725
|
+
serverName,
|
|
35726
|
+
reason: "a remote transport without a url",
|
|
35727
|
+
logger
|
|
35728
|
+
});
|
|
35729
|
+
continue;
|
|
35730
|
+
}
|
|
35731
|
+
const stated = type ?? transport;
|
|
35732
|
+
const remoteType = asTabnineRemoteType(stated, url);
|
|
35733
|
+
if (remoteType === void 0) {
|
|
35734
|
+
warnAndSkipMcpServer({
|
|
35735
|
+
toolName: "Tabnine CLI",
|
|
35736
|
+
serverName,
|
|
35737
|
+
reason: stated === void 0 ? "a WebSocket url, which Tabnine CLI's remote transports (http and sse) cannot reach" : `the "${stated}" transport, which Tabnine CLI does not offer for remote servers (only http and sse)`,
|
|
35738
|
+
logger
|
|
35739
|
+
});
|
|
35740
|
+
continue;
|
|
35741
|
+
}
|
|
35742
|
+
converted.url = url;
|
|
35743
|
+
if (remoteType !== null) converted.type = remoteType;
|
|
35744
|
+
} else {
|
|
35745
|
+
const [command, ...args] = resolveLocalMcpCommand(serverConfig);
|
|
35746
|
+
if (!command) {
|
|
35747
|
+
warnAndSkipMcpServer({
|
|
35748
|
+
toolName: "Tabnine CLI",
|
|
35749
|
+
serverName,
|
|
35750
|
+
reason: "a stdio transport without a command",
|
|
35751
|
+
logger
|
|
35752
|
+
});
|
|
35753
|
+
continue;
|
|
35754
|
+
}
|
|
35755
|
+
converted.command = command;
|
|
35756
|
+
if (args.length > 0) converted.args = args;
|
|
35757
|
+
}
|
|
35758
|
+
for (const [key, value] of Object.entries(rest)) {
|
|
35759
|
+
if (PROTOTYPE_POLLUTION_KEYS.has(key)) continue;
|
|
35760
|
+
converted[key] = (key === "env" || key === "headers") && isRecord$1(value) ? omitPrototypePollutionKeys(value) : value;
|
|
35761
|
+
}
|
|
35762
|
+
if (enabledTools !== void 0) converted.includeTools = enabledTools;
|
|
35763
|
+
if (disabledTools !== void 0) converted.excludeTools = disabledTools;
|
|
35764
|
+
result[serverName] = converted;
|
|
35765
|
+
}
|
|
35766
|
+
return result;
|
|
35767
|
+
}
|
|
35768
|
+
/**
|
|
35769
|
+
* Convert Tabnine's server map back to the canonical shape: `includeTools` /
|
|
35770
|
+
* `excludeTools` become `enabledTools` / `disabledTools`; `type` (`stdio`,
|
|
35771
|
+
* `sse`, `http`) and every other documented field are already canonical.
|
|
35772
|
+
*/
|
|
35773
|
+
function convertFromTabnineFormat(mcpServers) {
|
|
35774
|
+
if (!isMcpServers(mcpServers)) return {};
|
|
35775
|
+
const result = {};
|
|
35776
|
+
for (const [serverName, serverConfig] of Object.entries(mcpServers)) {
|
|
35777
|
+
if (PROTOTYPE_POLLUTION_KEYS.has(serverName) || !isRecord$1(serverConfig)) continue;
|
|
35778
|
+
const converted = {};
|
|
35779
|
+
for (const [key, value] of Object.entries(serverConfig)) {
|
|
35780
|
+
if (PROTOTYPE_POLLUTION_KEYS.has(key)) continue;
|
|
35781
|
+
if (key === "includeTools") converted.enabledTools = value;
|
|
35782
|
+
else if (key === "excludeTools") converted.disabledTools = value;
|
|
35783
|
+
else converted[key] = value;
|
|
35784
|
+
}
|
|
35785
|
+
result[serverName] = converted;
|
|
35786
|
+
}
|
|
35787
|
+
return result;
|
|
35788
|
+
}
|
|
35789
|
+
/**
|
|
35790
|
+
* Tabnine CLI MCP configuration.
|
|
35791
|
+
*
|
|
35792
|
+
* Tabnine CLI reads `mcpServers` from `<project>/.tabnine/agent/settings.json`
|
|
35793
|
+
* (project scope) and `~/.tabnine/agent/settings.json` (user scope); the
|
|
35794
|
+
* project entry wins when both define the same server name. Both files hold
|
|
35795
|
+
* the user's other settings (`general`, `tools`, `hooks`, ...), so writes go
|
|
35796
|
+
* through the shared-config gateway with `mcpServers` as the only owned key
|
|
35797
|
+
* and the file is never deleted.
|
|
35798
|
+
*
|
|
35799
|
+
* @see https://docs.tabnine.com/main/getting-started/tabnine-agent/mcp-intro-and-setup/mcp-server-config
|
|
35800
|
+
* @see https://docs.tabnine.com/main/getting-started/tabnine-cli/features/settings
|
|
35801
|
+
*/
|
|
35802
|
+
var TabnineMcp = class TabnineMcp extends ToolMcp {
|
|
35803
|
+
json;
|
|
35804
|
+
constructor(params) {
|
|
35805
|
+
super(params);
|
|
35806
|
+
this.json = parseSharedConfig({
|
|
35807
|
+
format: "json",
|
|
35808
|
+
fileContent: this.fileContent || "{}",
|
|
35809
|
+
filePath: join(this.relativeDirPath, this.relativeFilePath),
|
|
35810
|
+
invalidRootPolicy: "error"
|
|
35811
|
+
});
|
|
35812
|
+
}
|
|
35813
|
+
getJson() {
|
|
35814
|
+
return this.json;
|
|
35815
|
+
}
|
|
35816
|
+
/**
|
|
35817
|
+
* settings.json carries the user's other Tabnine settings, so it must not
|
|
35818
|
+
* be deleted.
|
|
35819
|
+
*/
|
|
35820
|
+
isDeletable() {
|
|
35821
|
+
return false;
|
|
35822
|
+
}
|
|
35823
|
+
static getSettablePaths(_options = {}) {
|
|
35824
|
+
return {
|
|
35825
|
+
relativeDirPath: TABNINE_AGENT_DIR_PATH,
|
|
35826
|
+
relativeFilePath: TABNINE_SETTINGS_FILE_NAME
|
|
35827
|
+
};
|
|
35828
|
+
}
|
|
35829
|
+
static async fromFile({ outputRoot = process.cwd(), validate = true, global = false }) {
|
|
35830
|
+
const paths = this.getSettablePaths({ global });
|
|
35831
|
+
const fileContent = await readFileContentOrNull(join(outputRoot, paths.relativeDirPath, paths.relativeFilePath)) ?? "{\"mcpServers\":{}}";
|
|
35832
|
+
return new TabnineMcp({
|
|
35833
|
+
outputRoot,
|
|
35834
|
+
relativeDirPath: paths.relativeDirPath,
|
|
35835
|
+
relativeFilePath: paths.relativeFilePath,
|
|
35836
|
+
fileContent,
|
|
35837
|
+
validate,
|
|
35838
|
+
global
|
|
35839
|
+
});
|
|
35840
|
+
}
|
|
35841
|
+
static async fromRulesyncMcp({ outputRoot = process.cwd(), rulesyncMcp, validate = true, global = false, logger }) {
|
|
35842
|
+
const paths = this.getSettablePaths({ global });
|
|
35843
|
+
const filePath = join(outputRoot, paths.relativeDirPath, paths.relativeFilePath);
|
|
35844
|
+
const existingContent = await readFileContentOrNull(filePath) ?? JSON.stringify({ mcpServers: {} }, null, 2);
|
|
35845
|
+
return new TabnineMcp({
|
|
35846
|
+
outputRoot,
|
|
35847
|
+
relativeDirPath: paths.relativeDirPath,
|
|
35848
|
+
relativeFilePath: paths.relativeFilePath,
|
|
35849
|
+
fileContent: applySharedConfigPatch({
|
|
35850
|
+
fileKey: sharedConfigFileKey(paths),
|
|
35851
|
+
feature: "mcp",
|
|
35852
|
+
existingContent,
|
|
35853
|
+
patch: { mcpServers: convertToTabnineFormat(rulesyncMcp.getMcpServers(), logger) },
|
|
35854
|
+
filePath
|
|
35855
|
+
}),
|
|
35856
|
+
validate,
|
|
35857
|
+
global
|
|
35858
|
+
});
|
|
35859
|
+
}
|
|
35860
|
+
toRulesyncMcp() {
|
|
35861
|
+
const mcpServers = convertFromTabnineFormat(this.json.mcpServers);
|
|
35862
|
+
return this.toRulesyncMcpDefault({ fileContent: JSON.stringify({ mcpServers }, null, 2) });
|
|
35863
|
+
}
|
|
35864
|
+
validate() {
|
|
35865
|
+
return {
|
|
35866
|
+
success: true,
|
|
35867
|
+
error: null
|
|
35868
|
+
};
|
|
35869
|
+
}
|
|
35870
|
+
static forDeletion({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath, global = false }) {
|
|
35871
|
+
return new TabnineMcp({
|
|
35872
|
+
outputRoot,
|
|
35873
|
+
relativeDirPath,
|
|
35874
|
+
relativeFilePath,
|
|
35875
|
+
fileContent: "{}",
|
|
35876
|
+
validate: false,
|
|
35877
|
+
global
|
|
35878
|
+
});
|
|
35879
|
+
}
|
|
35880
|
+
};
|
|
35881
|
+
//#endregion
|
|
33982
35882
|
//#region src/features/mcp/takt-mcp.ts
|
|
33983
35883
|
/**
|
|
33984
35884
|
* MCP adapter for Takt (`.takt/config.yaml` project / `~/.takt/config.yaml`
|
|
@@ -34938,6 +36838,15 @@ const toolMcpFactories = /* @__PURE__ */ new Map([
|
|
|
34938
36838
|
supportsDisabledTools: false
|
|
34939
36839
|
}
|
|
34940
36840
|
}],
|
|
36841
|
+
["bob", {
|
|
36842
|
+
class: BobMcp,
|
|
36843
|
+
meta: {
|
|
36844
|
+
supportsProject: true,
|
|
36845
|
+
supportsGlobal: true,
|
|
36846
|
+
supportsEnabledTools: false,
|
|
36847
|
+
supportsDisabledTools: false
|
|
36848
|
+
}
|
|
36849
|
+
}],
|
|
34941
36850
|
["claudecode", {
|
|
34942
36851
|
class: ClaudecodeMcp,
|
|
34943
36852
|
meta: {
|
|
@@ -35001,6 +36910,15 @@ const toolMcpFactories = /* @__PURE__ */ new Map([
|
|
|
35001
36910
|
supportsDisabledTools: false
|
|
35002
36911
|
}
|
|
35003
36912
|
}],
|
|
36913
|
+
["cortexcode", {
|
|
36914
|
+
class: CortexcodeMcp,
|
|
36915
|
+
meta: {
|
|
36916
|
+
supportsProject: false,
|
|
36917
|
+
supportsGlobal: true,
|
|
36918
|
+
supportsEnabledTools: false,
|
|
36919
|
+
supportsDisabledTools: false
|
|
36920
|
+
}
|
|
36921
|
+
}],
|
|
35004
36922
|
["cursor", {
|
|
35005
36923
|
class: CursorMcp,
|
|
35006
36924
|
meta: {
|
|
@@ -35172,6 +37090,15 @@ const toolMcpFactories = /* @__PURE__ */ new Map([
|
|
|
35172
37090
|
supportsDisabledTools: false
|
|
35173
37091
|
}
|
|
35174
37092
|
}],
|
|
37093
|
+
["tabnine", {
|
|
37094
|
+
class: TabnineMcp,
|
|
37095
|
+
meta: {
|
|
37096
|
+
supportsProject: true,
|
|
37097
|
+
supportsGlobal: true,
|
|
37098
|
+
supportsEnabledTools: true,
|
|
37099
|
+
supportsDisabledTools: true
|
|
37100
|
+
}
|
|
37101
|
+
}],
|
|
35175
37102
|
["takt", {
|
|
35176
37103
|
class: TaktMcp,
|
|
35177
37104
|
meta: {
|
|
@@ -36376,7 +38303,7 @@ const ANTIGRAVITY_CLI_OVERRIDE_KEYS = [
|
|
|
36376
38303
|
"allowNonWorkspaceAccess",
|
|
36377
38304
|
"agentMode"
|
|
36378
38305
|
];
|
|
36379
|
-
const moduleLogger$
|
|
38306
|
+
const moduleLogger$4 = fallbackLogger;
|
|
36380
38307
|
/**
|
|
36381
38308
|
* Narrow an imported `settings.json` value to one of the documented values of
|
|
36382
38309
|
* an enum-typed override key. The override schema declares `toolPermission`,
|
|
@@ -36390,7 +38317,7 @@ const moduleLogger$3 = fallbackLogger;
|
|
|
36390
38317
|
function pickDocumentedValue({ key, value, documented }) {
|
|
36391
38318
|
if (typeof value !== "string") return void 0;
|
|
36392
38319
|
if (documented.includes(value)) return value;
|
|
36393
|
-
moduleLogger$
|
|
38320
|
+
moduleLogger$4.warn(`Antigravity CLI permissions: dropping '${key}: ${value}' on import — it is not one of the documented values (${documented.join(", ")}), and carrying it through would fail validation of the whole permissions file. Author it under the 'antigravity-cli' override by hand once rulesync supports it.`);
|
|
36394
38321
|
}
|
|
36395
38322
|
/**
|
|
36396
38323
|
* Deliberate mapping from rulesync canonical permission categories to the
|
|
@@ -36862,7 +38789,7 @@ function convertAntigravityIdeToRulesyncPermissions(params) {
|
|
|
36862
38789
|
}
|
|
36863
38790
|
//#endregion
|
|
36864
38791
|
//#region src/features/permissions/augmentcode-permissions.ts
|
|
36865
|
-
const moduleLogger$
|
|
38792
|
+
const moduleLogger$3 = fallbackLogger;
|
|
36866
38793
|
z.enum([
|
|
36867
38794
|
"allow",
|
|
36868
38795
|
"deny",
|
|
@@ -37161,7 +39088,7 @@ var AugmentcodePermissions = class AugmentcodePermissions extends ToolPermission
|
|
|
37161
39088
|
const specialEntries = allEntries.filter((entry) => isSpecialEntry(entry));
|
|
37162
39089
|
const result = { ...convertAugmentToRulesyncPermissions({
|
|
37163
39090
|
entries: allEntries.filter((entry) => !isSpecialEntry(entry)),
|
|
37164
|
-
logger: moduleLogger$
|
|
39091
|
+
logger: moduleLogger$3
|
|
37165
39092
|
}) };
|
|
37166
39093
|
if (specialEntries.length > 0) result.augmentcode = { toolPermissions: specialEntries };
|
|
37167
39094
|
return this.toRulesyncPermissionsDefault({ fileContent: JSON.stringify(result, null, 2) });
|
|
@@ -37535,6 +39462,14 @@ function collectRestrictionLosingSandboxEntries({ existing, merged, paths, toolL
|
|
|
37535
39462
|
return entries;
|
|
37536
39463
|
}
|
|
37537
39464
|
/**
|
|
39465
|
+
* The `'<label>' — <reason>` list a trust-affecting warning ends with, shared so
|
|
39466
|
+
* an adapter that phrases the rest of the sentence itself (an import that lifts
|
|
39467
|
+
* the settings rather than writes them) prints the entries the same way.
|
|
39468
|
+
*/
|
|
39469
|
+
function formatTrustAffectingEntries(entries) {
|
|
39470
|
+
return entries.map(({ label, reason }) => `'${label}' — ${reason}`).join("; ");
|
|
39471
|
+
}
|
|
39472
|
+
/**
|
|
37538
39473
|
* The one warning that names every trust-affecting setting this generate wrote
|
|
37539
39474
|
* to `relativeFilePath`. Emitted once per file: the individual reasons are what
|
|
37540
39475
|
* matter, but the "review this as you would a hook" framing only needs saying
|
|
@@ -37545,7 +39480,7 @@ function collectRestrictionLosingSandboxEntries({ existing, merged, paths, toolL
|
|
|
37545
39480
|
function warnOnTrustAffectingEntries({ toolLabel, noun = "setting", entries, relativeFilePath, logger }) {
|
|
37546
39481
|
if (entries.length === 0) return;
|
|
37547
39482
|
const one = entries.length === 1;
|
|
37548
|
-
const details = entries
|
|
39483
|
+
const details = formatTrustAffectingEntries(entries);
|
|
37549
39484
|
logger?.warn(`${toolLabel} permissions: writing ${entries.length} trust-affecting ${noun}${one ? "" : "s"} to ${relativeFilePath}; review ${one ? "it" : "them"} as you would a hook, especially if this permissions file came from 'rulesync fetch'. ${details}.`);
|
|
37550
39485
|
}
|
|
37551
39486
|
//#endregion
|
|
@@ -43667,7 +45602,7 @@ const QwenSettingsPermissionsSchema = z.looseObject({
|
|
|
43667
45602
|
deny: z.optional(z.array(z.string()))
|
|
43668
45603
|
});
|
|
43669
45604
|
const QwenSettingsSchema = z.looseObject({ permissions: z.optional(QwenSettingsPermissionsSchema) });
|
|
43670
|
-
const moduleLogger$
|
|
45605
|
+
const moduleLogger$2 = fallbackLogger;
|
|
43671
45606
|
/**
|
|
43672
45607
|
* Mapping from rulesync canonical tool category names (lowercase) to Qwen Code tool names (PascalCase).
|
|
43673
45608
|
* Unknown names pass through as-is (e.g., mcp__server__tool).
|
|
@@ -44030,7 +45965,7 @@ function warnAboutImportedScopedKeys(groups) {
|
|
|
44030
45965
|
if (!announceOnImport) continue;
|
|
44031
45966
|
if (announceOnlyGrants && !grantsSomething(value, scopedKey)) continue;
|
|
44032
45967
|
const globalNote = scopedKey.globalNote ?? QWEN_SCOPE_RULES[rule].globalNote;
|
|
44033
|
-
moduleLogger$
|
|
45968
|
+
moduleLogger$2.warn(`Qwen permissions: imported ${quoteQualifiedKey({
|
|
44034
45969
|
groupName,
|
|
44035
45970
|
key
|
|
44036
45971
|
})} = ${quoteValueForWarning(value)}. ${globalNote} Review it before generating with the global scope.`);
|
|
@@ -44223,7 +46158,7 @@ function convertRulesyncToQwenPermissions(config) {
|
|
|
44223
46158
|
}
|
|
44224
46159
|
function convertQwenToRulesyncPermissions(params) {
|
|
44225
46160
|
const permission = {};
|
|
44226
|
-
const logger = params.logger ?? moduleLogger$
|
|
46161
|
+
const logger = params.logger ?? moduleLogger$2;
|
|
44227
46162
|
const processEntries = (entries, action) => {
|
|
44228
46163
|
for (const entry of entries) {
|
|
44229
46164
|
const parsed = parseQwenPermissionEntry(entry, { logger });
|
|
@@ -45549,6 +47484,585 @@ function isPermissionAction(value) {
|
|
|
45549
47484
|
return PermissionActionSchema.safeParse(value).success;
|
|
45550
47485
|
}
|
|
45551
47486
|
//#endregion
|
|
47487
|
+
//#region src/features/permissions/tabnine-permissions.ts
|
|
47488
|
+
/** The `settings.json` key group that carries the two tool lists. */
|
|
47489
|
+
const TOOLS_KEY = "tools";
|
|
47490
|
+
/** `tools.allowed`: tool names (and `run_shell_command(<prefix>)`) that skip the confirmation prompt. */
|
|
47491
|
+
const ALLOWED_KEY = "allowed";
|
|
47492
|
+
/** `tools.exclude`: tool names removed from discovery entirely. */
|
|
47493
|
+
const EXCLUDE_KEY = "exclude";
|
|
47494
|
+
/** The `settings.json` key group whose keys the `tabnine` override may author. */
|
|
47495
|
+
const GENERAL_KEY = "general";
|
|
47496
|
+
/** The Tabnine CLI built-in that runs shell commands. */
|
|
47497
|
+
const SHELL_TOOL_NAME = "run_shell_command";
|
|
47498
|
+
/**
|
|
47499
|
+
* The paths of the `tabnine` override that are refused with a warning rather
|
|
47500
|
+
* than written — the line Claude Code's sandbox paths draw. Under `tools`, the
|
|
47501
|
+
* values Tabnine CLI spawns as a command (`discoveryCommand` and `callCommand`
|
|
47502
|
+
* at startup, `shell.pager` on every shell output, `sandbox.command` to start
|
|
47503
|
+
* the sandbox); under `general`, the host every login and API call goes to
|
|
47504
|
+
* (`tabnineHost`) and the editor executable (`preferredEditor`, "any editor
|
|
47505
|
+
* path"). A fetched `.rulesync/permissions.jsonc` must not be able to point
|
|
47506
|
+
* Tabnine CLI at an executable, or a server, of its choosing.
|
|
47507
|
+
* @see https://docs.tabnine.com/main/getting-started/tabnine-cli/features/settings/settings-reference
|
|
47508
|
+
*/
|
|
47509
|
+
const REFUSED_OVERRIDE_PATHS = {
|
|
47510
|
+
[TOOLS_KEY]: [
|
|
47511
|
+
["discoveryCommand"],
|
|
47512
|
+
["callCommand"],
|
|
47513
|
+
["shell", "pager"],
|
|
47514
|
+
["sandbox", "command"]
|
|
47515
|
+
],
|
|
47516
|
+
[GENERAL_KEY]: [["tabnineHost"], ["preferredEditor"]]
|
|
47517
|
+
};
|
|
47518
|
+
/** `tools.sandbox`: `true` keeps tool calls contained; anything else disables or picks the sandbox. */
|
|
47519
|
+
const SANDBOX_KEY = "sandbox";
|
|
47520
|
+
/** `general.defaultApprovalMode`: the modes that still ask before a tool call runs. */
|
|
47521
|
+
const DEFAULT_APPROVAL_MODE_KEY = "defaultApprovalMode";
|
|
47522
|
+
const PROMPTING_APPROVAL_MODES = /* @__PURE__ */ new Set(["default", "plan"]);
|
|
47523
|
+
/**
|
|
47524
|
+
* Canonical categories with a one-to-one Tabnine CLI built-in tool. `bash` is
|
|
47525
|
+
* handled separately because it is the only tool whose list entries carry a
|
|
47526
|
+
* command prefix.
|
|
47527
|
+
*
|
|
47528
|
+
* @see https://docs.tabnine.com/main/getting-started/tabnine-cli/features/built-in-tools
|
|
47529
|
+
*/
|
|
47530
|
+
const CANONICAL_TO_TABNINE_TOOL_NAMES = {
|
|
47531
|
+
read: "read_file",
|
|
47532
|
+
edit: "replace",
|
|
47533
|
+
write: "write_file",
|
|
47534
|
+
webfetch: "web_fetch",
|
|
47535
|
+
websearch: "google_web_search",
|
|
47536
|
+
grep: "grep_search",
|
|
47537
|
+
glob: "glob"
|
|
47538
|
+
};
|
|
47539
|
+
const TABNINE_TO_CANONICAL_TOOL_NAMES = Object.fromEntries(Object.entries(CANONICAL_TO_TABNINE_TOOL_NAMES).map(([canonical, tabnine]) => [tabnine, canonical]));
|
|
47540
|
+
const moduleLogger$1 = fallbackLogger;
|
|
47541
|
+
/**
|
|
47542
|
+
* Turn a canonical `bash` glob into the command prefix a
|
|
47543
|
+
* `run_shell_command(<prefix>)` entry carries. Tabnine matches the prefix
|
|
47544
|
+
* against the start of the command line, so only two glob shapes map cleanly:
|
|
47545
|
+
* `<prefix> *` and a bare `<prefix>` with no wildcard at all. Anything else
|
|
47546
|
+
* (`git * push`, `*.sh`, `npm run:*`) matches commands a prefix cannot name, so
|
|
47547
|
+
* it is reported and left out rather than approximated. `*` alone names the
|
|
47548
|
+
* tool itself and is returned as the empty prefix.
|
|
47549
|
+
*/
|
|
47550
|
+
function toShellPrefix(pattern) {
|
|
47551
|
+
if (pattern === "*") return "";
|
|
47552
|
+
const prefix = pattern.endsWith(" *") ? pattern.slice(0, -2) : pattern;
|
|
47553
|
+
if (prefix.length === 0 || /[*?[\]]/.test(prefix)) return;
|
|
47554
|
+
return prefix;
|
|
47555
|
+
}
|
|
47556
|
+
/**
|
|
47557
|
+
* The Tabnine tool a canonical category names. An unknown category is passed
|
|
47558
|
+
* through as a tool name (that is how an MCP tool is named); the read is an
|
|
47559
|
+
* own-property lookup so a category spelled `toString` does not pick up an
|
|
47560
|
+
* `Object.prototype` member.
|
|
47561
|
+
*/
|
|
47562
|
+
function toTabnineToolName(category) {
|
|
47563
|
+
return lookupOwn({
|
|
47564
|
+
record: CANONICAL_TO_TABNINE_TOOL_NAMES,
|
|
47565
|
+
key: category
|
|
47566
|
+
}) ?? category;
|
|
47567
|
+
}
|
|
47568
|
+
/**
|
|
47569
|
+
* The widest glob a `bash` pattern stands for once it is written as a
|
|
47570
|
+
* `run_shell_command(<prefix>)` entry: the prefix matches the start of the
|
|
47571
|
+
* command line, so a bare `pnpm` also covers `pnpm install`. Used only to
|
|
47572
|
+
* compare allows against the restrictions (`bash` deny and `ask` rules, and
|
|
47573
|
+
* the rules of `*`), where widening can only withhold more, never fail open.
|
|
47574
|
+
*/
|
|
47575
|
+
function widenToPrefixGlob(pattern) {
|
|
47576
|
+
const prefix = toShellPrefix(pattern);
|
|
47577
|
+
if (prefix === void 0) return pattern;
|
|
47578
|
+
return prefix === "" ? "*" : `${prefix}*`;
|
|
47579
|
+
}
|
|
47580
|
+
function toShellEntry(prefix) {
|
|
47581
|
+
return prefix === "" ? SHELL_TOOL_NAME : `${SHELL_TOOL_NAME}(${prefix})`;
|
|
47582
|
+
}
|
|
47583
|
+
/** The `bash` pattern a `run_shell_command(<prefix>)` entry stands for. */
|
|
47584
|
+
function toShellPattern(prefix) {
|
|
47585
|
+
return prefix === void 0 || prefix === "" ? "*" : `${prefix} *`;
|
|
47586
|
+
}
|
|
47587
|
+
/**
|
|
47588
|
+
* Split a `tools.allowed`/`tools.exclude` entry into its tool name and the
|
|
47589
|
+
* optional `(<prefix>)` suffix. An entry whose parenthesis never closes, or
|
|
47590
|
+
* that carries text after the closing one, is malformed and yields `undefined`.
|
|
47591
|
+
*/
|
|
47592
|
+
function parseTabnineEntry(entry) {
|
|
47593
|
+
const open = entry.indexOf("(");
|
|
47594
|
+
if (open === -1) return entry.length > 0 ? {
|
|
47595
|
+
toolName: entry,
|
|
47596
|
+
prefix: void 0
|
|
47597
|
+
} : void 0;
|
|
47598
|
+
if (open === 0 || !entry.endsWith(")")) return;
|
|
47599
|
+
return {
|
|
47600
|
+
toolName: entry.slice(0, open),
|
|
47601
|
+
prefix: entry.slice(open + 1, -1)
|
|
47602
|
+
};
|
|
47603
|
+
}
|
|
47604
|
+
/**
|
|
47605
|
+
* `record` without the value at `path`, when there is one, and without any
|
|
47606
|
+
* container the removal emptied. Containers on the way are copied, never
|
|
47607
|
+
* mutated, so the override the caller read stays intact.
|
|
47608
|
+
*/
|
|
47609
|
+
function omitPath({ record, path }) {
|
|
47610
|
+
const [head, ...rest] = path;
|
|
47611
|
+
if (head === void 0 || !Object.hasOwn(record, head)) return {
|
|
47612
|
+
record,
|
|
47613
|
+
removed: false
|
|
47614
|
+
};
|
|
47615
|
+
if (rest.length === 0) {
|
|
47616
|
+
const { [head]: _removed, ...others } = record;
|
|
47617
|
+
return {
|
|
47618
|
+
record: others,
|
|
47619
|
+
removed: true
|
|
47620
|
+
};
|
|
47621
|
+
}
|
|
47622
|
+
const child = record[head];
|
|
47623
|
+
if (!isRecord$1(child)) return {
|
|
47624
|
+
record,
|
|
47625
|
+
removed: false
|
|
47626
|
+
};
|
|
47627
|
+
const inner = omitPath({
|
|
47628
|
+
record: child,
|
|
47629
|
+
path: rest
|
|
47630
|
+
});
|
|
47631
|
+
if (!inner.removed) return {
|
|
47632
|
+
record,
|
|
47633
|
+
removed: false
|
|
47634
|
+
};
|
|
47635
|
+
if (Object.keys(inner.record).length === 0) {
|
|
47636
|
+
const { [head]: _emptied, ...others } = record;
|
|
47637
|
+
return {
|
|
47638
|
+
record: others,
|
|
47639
|
+
removed: true
|
|
47640
|
+
};
|
|
47641
|
+
}
|
|
47642
|
+
return {
|
|
47643
|
+
record: {
|
|
47644
|
+
...record,
|
|
47645
|
+
[head]: inner.record
|
|
47646
|
+
},
|
|
47647
|
+
removed: true
|
|
47648
|
+
};
|
|
47649
|
+
}
|
|
47650
|
+
/**
|
|
47651
|
+
* The `tabnine.<group>` record with every refused path removed, and the dotted
|
|
47652
|
+
* names of the paths that were there. Nothing else of the group is touched:
|
|
47653
|
+
* the rest is written verbatim (and announced where it widens trust).
|
|
47654
|
+
*/
|
|
47655
|
+
function stripRefusedPaths({ group, record }) {
|
|
47656
|
+
const refused = [];
|
|
47657
|
+
let stripped = record;
|
|
47658
|
+
for (const path of REFUSED_OVERRIDE_PATHS[group]) {
|
|
47659
|
+
const result = omitPath({
|
|
47660
|
+
record: stripped,
|
|
47661
|
+
path
|
|
47662
|
+
});
|
|
47663
|
+
if (result.removed) {
|
|
47664
|
+
refused.push(`${group}.${path.join(".")}`);
|
|
47665
|
+
stripped = result.record;
|
|
47666
|
+
}
|
|
47667
|
+
}
|
|
47668
|
+
return {
|
|
47669
|
+
record: stripped,
|
|
47670
|
+
refused
|
|
47671
|
+
};
|
|
47672
|
+
}
|
|
47673
|
+
/**
|
|
47674
|
+
* The `tools` and `general` groups of an override with the refused paths taken
|
|
47675
|
+
* out, and every refused path named. A `general` that is absent or that the
|
|
47676
|
+
* refusals emptied is reported as absent so no `general: {}` is written or
|
|
47677
|
+
* lifted. (The `TabninePermissionsOverrideSchema` already rejects a group that
|
|
47678
|
+
* is not an object; the `isRecord` guards only keep an unvalidated file from
|
|
47679
|
+
* throwing.)
|
|
47680
|
+
*/
|
|
47681
|
+
function stripRefusedOverridePaths(override) {
|
|
47682
|
+
const tools = stripRefusedPaths({
|
|
47683
|
+
group: TOOLS_KEY,
|
|
47684
|
+
record: isRecord$1(override[TOOLS_KEY]) ? override[TOOLS_KEY] : {}
|
|
47685
|
+
});
|
|
47686
|
+
const general = isRecord$1(override[GENERAL_KEY]) ? stripRefusedPaths({
|
|
47687
|
+
group: GENERAL_KEY,
|
|
47688
|
+
record: override[GENERAL_KEY]
|
|
47689
|
+
}) : void 0;
|
|
47690
|
+
return {
|
|
47691
|
+
tools: tools.record,
|
|
47692
|
+
general: general !== void 0 && Object.keys(general.record).length > 0 ? general.record : void 0,
|
|
47693
|
+
refused: [...tools.refused, ...general?.refused ?? []]
|
|
47694
|
+
};
|
|
47695
|
+
}
|
|
47696
|
+
/**
|
|
47697
|
+
* The settings of a `tabnine` override that widen what Tabnine CLI does without
|
|
47698
|
+
* asking, so the generate can name them. `tools.allowed` counts because an
|
|
47699
|
+
* override-authored entry skips the confirmation prompt on the strength of the
|
|
47700
|
+
* override alone (its `run_shell_command(...)` entries are not here: they are
|
|
47701
|
+
* compared as `bash` allows and withheld like them); `tools.sandbox` and
|
|
47702
|
+
* `general.defaultApprovalMode` decide whether tool calls are contained and
|
|
47703
|
+
* whether they are confirmed at all. Called on the override, not the file, so a
|
|
47704
|
+
* value the file already held is not reported as something rulesync opened.
|
|
47705
|
+
*/
|
|
47706
|
+
function collectTrustAffectingOverrideEntries({ tools, general }) {
|
|
47707
|
+
const entries = [];
|
|
47708
|
+
const allowed = isStringArray$2(tools[ALLOWED_KEY]) ? tools[ALLOWED_KEY] : [];
|
|
47709
|
+
if (allowed.length > 0) entries.push({
|
|
47710
|
+
label: `tools.allowed (${allowed.map(quoteValueForWarning).join(", ")})`,
|
|
47711
|
+
reason: "auto-approves what it names as the override spells it; a non-shell entry has no canonical rule to be checked against"
|
|
47712
|
+
});
|
|
47713
|
+
if (Object.hasOwn(tools, SANDBOX_KEY) && isNotTrue(tools[SANDBOX_KEY])) entries.push({
|
|
47714
|
+
label: `tools.sandbox = ${quoteValueForWarning(tools[SANDBOX_KEY])}`,
|
|
47715
|
+
reason: "is not the plain `true` that keeps tool calls contained, so it either disables the sandbox or picks the command, image, paths and network that contain them"
|
|
47716
|
+
});
|
|
47717
|
+
const approvalMode = general?.[DEFAULT_APPROVAL_MODE_KEY];
|
|
47718
|
+
if (general !== void 0 && Object.hasOwn(general, DEFAULT_APPROVAL_MODE_KEY) && !PROMPTING_APPROVAL_MODES.has(approvalMode)) entries.push({
|
|
47719
|
+
label: `general.defaultApprovalMode = ${quoteValueForWarning(approvalMode)}`,
|
|
47720
|
+
reason: "auto-approves tool calls instead of asking; only `default` and `plan` keep the prompt"
|
|
47721
|
+
});
|
|
47722
|
+
return entries;
|
|
47723
|
+
}
|
|
47724
|
+
/**
|
|
47725
|
+
* What a later generate would announce from the override an import builds,
|
|
47726
|
+
* said now, so the file the import writes into `.rulesync/` is read with the
|
|
47727
|
+
* same eyes as one that arrived with a clone. `refused` names the paths the
|
|
47728
|
+
* import left in `settings.json`, the ones generate refuses to write — an
|
|
47729
|
+
* override must not carry a value that only ever produces a warning.
|
|
47730
|
+
*/
|
|
47731
|
+
function announceLiftedOverride({ tools, general, refused }) {
|
|
47732
|
+
if (refused.length > 0) {
|
|
47733
|
+
const one = refused.length === 1;
|
|
47734
|
+
moduleLogger$1.info(`Tabnine CLI permissions: left ${refused.join(", ")} in settings.json rather than lifting ${one ? "it" : "them"} into the tabnine override; Tabnine CLI runs ${one ? "that value" : "those values"} as a command or sends its traffic there, and 'rulesync generate' refuses to write ${one ? "it" : "them"} from the override.`);
|
|
47735
|
+
}
|
|
47736
|
+
const trustAffecting = collectTrustAffectingOverrideEntries({
|
|
47737
|
+
tools,
|
|
47738
|
+
general
|
|
47739
|
+
});
|
|
47740
|
+
if (trustAffecting.length > 0) {
|
|
47741
|
+
const one = trustAffecting.length === 1;
|
|
47742
|
+
moduleLogger$1.warn(`Tabnine CLI permissions: the tabnine override now carries ${trustAffecting.length} trust-affecting setting${one ? "" : "s"} that 'rulesync generate' writes back as authored: ${formatTrustAffectingEntries(trustAffecting)}.`);
|
|
47743
|
+
}
|
|
47744
|
+
}
|
|
47745
|
+
/**
|
|
47746
|
+
* Build the two tool lists from the canonical block; the rules that could not
|
|
47747
|
+
* be carried are reported through `logger` here.
|
|
47748
|
+
*/
|
|
47749
|
+
function buildToolLists({ permission, overrideShellAllowPatterns, logger }) {
|
|
47750
|
+
const allowed = [];
|
|
47751
|
+
const exclude = [];
|
|
47752
|
+
const { rules: canonicalRules, ignoredAllToolsAllowPatterns } = collectShellCommandRules(permission);
|
|
47753
|
+
const { allow: shellAllow, deny: shellDeny, shadowedAllowPatterns, unwrittenDenyPatterns, unenforcedAllToolsAskPatterns, intersectionBudgetExhausted } = partitionCommandRules({
|
|
47754
|
+
rules: [...canonicalRules, ...overrideShellAllowPatterns.map((pattern) => ({
|
|
47755
|
+
pattern,
|
|
47756
|
+
action: "allow",
|
|
47757
|
+
fromAllToolsCategory: false
|
|
47758
|
+
}))],
|
|
47759
|
+
writesAllToolsDeny: false,
|
|
47760
|
+
normalizePattern: widenToPrefixGlob
|
|
47761
|
+
});
|
|
47762
|
+
warnAboutUnwrittenCommandRules({
|
|
47763
|
+
toolLabel: "Tabnine CLI",
|
|
47764
|
+
surfaceLabel: "tools.allowed/tools.exclude",
|
|
47765
|
+
foreignRestrictingCategories: [],
|
|
47766
|
+
shadowedAllowPatterns,
|
|
47767
|
+
unwrittenDenyPatterns,
|
|
47768
|
+
unwrittenDenyReason: "tools.exclude removes a tool from discovery, and a pattern written under '*' need not be a command at all.",
|
|
47769
|
+
unenforcedAllToolsAskPatterns,
|
|
47770
|
+
ignoredAllToolsAllowPatterns,
|
|
47771
|
+
intersectionBudgetExhausted,
|
|
47772
|
+
logger
|
|
47773
|
+
});
|
|
47774
|
+
const unmappedShellPatterns = [];
|
|
47775
|
+
for (const pattern of shellDeny) {
|
|
47776
|
+
const prefix = toShellPrefix(pattern);
|
|
47777
|
+
if (prefix === void 0) {
|
|
47778
|
+
unmappedShellPatterns.push(pattern);
|
|
47779
|
+
continue;
|
|
47780
|
+
}
|
|
47781
|
+
exclude.push(toShellEntry(prefix));
|
|
47782
|
+
}
|
|
47783
|
+
const shadowingShellDenies = createShadowingRestrictionsTest(shellDeny.map((pattern) => ({
|
|
47784
|
+
pattern,
|
|
47785
|
+
fromAllToolsCategory: false
|
|
47786
|
+
})), { normalizePattern: widenToPrefixGlob });
|
|
47787
|
+
const withheldAllowPatterns = [];
|
|
47788
|
+
for (const pattern of shellAllow) {
|
|
47789
|
+
const prefix = toShellPrefix(pattern);
|
|
47790
|
+
if (prefix === void 0) {
|
|
47791
|
+
unmappedShellPatterns.push(pattern);
|
|
47792
|
+
continue;
|
|
47793
|
+
}
|
|
47794
|
+
if (shadowingShellDenies(pattern).length > 0) {
|
|
47795
|
+
withheldAllowPatterns.push(pattern);
|
|
47796
|
+
continue;
|
|
47797
|
+
}
|
|
47798
|
+
allowed.push(toShellEntry(prefix));
|
|
47799
|
+
}
|
|
47800
|
+
if (unmappedShellPatterns.length > 0) warnWithFallback(logger, `Tabnine CLI permissions: skipped ${unmappedShellPatterns.length} 'bash' rule(s) whose pattern is not a command prefix (${unmappedShellPatterns.map(quoteValueForWarning).join(", ")}). run_shell_command(<prefix>) matches the start of the command line, so only '<prefix> *' and a bare '<prefix>' can be written.`);
|
|
47801
|
+
if (withheldAllowPatterns.length > 0) warnWithFallback(logger, `Tabnine CLI permissions: withheld ${withheldAllowPatterns.length} 'bash' allow rule(s) (${withheldAllowPatterns.map(quoteValueForWarning).join(", ")}) that overlap a 'bash' deny rule; writing them could auto-approve the denied commands, since tools.exclude is not documented to narrow run_shell_command to a prefix.`);
|
|
47802
|
+
const unmappedPatterns = [];
|
|
47803
|
+
for (const [category, categoryRules] of Object.entries(permission)) {
|
|
47804
|
+
if (category === "bash" || category === "*") continue;
|
|
47805
|
+
const toolName = toTabnineToolName(category);
|
|
47806
|
+
for (const [pattern, action] of Object.entries(categoryRules)) {
|
|
47807
|
+
if (pattern !== "*") {
|
|
47808
|
+
unmappedPatterns.push(`${category}: ${pattern}`);
|
|
47809
|
+
continue;
|
|
47810
|
+
}
|
|
47811
|
+
if (action === "allow") allowed.push(toolName);
|
|
47812
|
+
else if (action === "deny") exclude.push(toolName);
|
|
47813
|
+
}
|
|
47814
|
+
}
|
|
47815
|
+
if (unmappedPatterns.length > 0) warnWithFallback(logger, `Tabnine CLI permissions: skipped ${unmappedPatterns.length} rule(s) with a pattern other than '*' (${unmappedPatterns.map(quoteValueForWarning).join(", ")}). tools.allowed/tools.exclude name whole tools; only run_shell_command takes a prefix.`);
|
|
47816
|
+
return {
|
|
47817
|
+
allowed: uniq(allowed),
|
|
47818
|
+
exclude: uniq(exclude)
|
|
47819
|
+
};
|
|
47820
|
+
}
|
|
47821
|
+
/**
|
|
47822
|
+
* Permissions generator for the Tabnine CLI.
|
|
47823
|
+
*
|
|
47824
|
+
* Tabnine CLI keeps its tool policy in `.tabnine/agent/settings.json` (project)
|
|
47825
|
+
* and `~/.tabnine/agent/settings.json` (user), a shared file that also carries
|
|
47826
|
+
* `mcpServers`, `hooks`, `general.*`, `context.*` and more, so writes go through
|
|
47827
|
+
* the shared-config gateway as a deep merge and the file is never deleted.
|
|
47828
|
+
*
|
|
47829
|
+
* Two keys are driven by the canonical block:
|
|
47830
|
+
* - `tools.allowed` — `allow` rules. A tool name skips the confirmation prompt;
|
|
47831
|
+
* `run_shell_command(<prefix>)` narrows that to one command prefix.
|
|
47832
|
+
* - `tools.exclude` — `deny` rules. A tool name removes the tool from discovery.
|
|
47833
|
+
* A `bash` deny is written as `run_shell_command(<prefix>)` there, the form
|
|
47834
|
+
* Gemini CLI's `excludeTools` (which Tabnine derives from) honors, but Tabnine
|
|
47835
|
+
* documents the prefix for `tools.allowed` only — so the entry is not relied
|
|
47836
|
+
* on, and every allow the deny overlaps is withheld as well.
|
|
47837
|
+
*
|
|
47838
|
+
* `ask` writes nothing: a tool that is in neither list keeps Tabnine's default
|
|
47839
|
+
* prompt. Every other `tools.*` key (`core`, `shell.*`, `enableWebTools`, ...)
|
|
47840
|
+
* and the `general.*` group are authored through the `tabnine` override, which
|
|
47841
|
+
* is deep-merged beneath the two canonical lists; entries in the lists that
|
|
47842
|
+
* cannot be mapped back (a prefix on a non-shell tool, an MCP tool name) round
|
|
47843
|
+
* trip through the override too.
|
|
47844
|
+
*
|
|
47845
|
+
* @see https://docs.tabnine.com/main/getting-started/tabnine-cli/features/built-in-tools
|
|
47846
|
+
* @see https://docs.tabnine.com/main/getting-started/tabnine-cli/features/settings/settings-reference
|
|
47847
|
+
*/
|
|
47848
|
+
var TabninePermissions = class TabninePermissions extends ToolPermissions {
|
|
47849
|
+
constructor(params) {
|
|
47850
|
+
super({
|
|
47851
|
+
...params,
|
|
47852
|
+
fileContent: params.fileContent ?? "{}"
|
|
47853
|
+
});
|
|
47854
|
+
}
|
|
47855
|
+
/**
|
|
47856
|
+
* `settings.json` holds unrelated user settings (`mcpServers`, `hooks`,
|
|
47857
|
+
* `general`, ...), so it must not be deleted.
|
|
47858
|
+
*/
|
|
47859
|
+
isDeletable() {
|
|
47860
|
+
return false;
|
|
47861
|
+
}
|
|
47862
|
+
static getSettablePaths(_options = {}) {
|
|
47863
|
+
return {
|
|
47864
|
+
relativeDirPath: TABNINE_AGENT_DIR_PATH,
|
|
47865
|
+
relativeFilePath: TABNINE_SETTINGS_FILE_NAME
|
|
47866
|
+
};
|
|
47867
|
+
}
|
|
47868
|
+
static async fromFile({ outputRoot = process.cwd(), validate = true, global = false }) {
|
|
47869
|
+
const paths = TabninePermissions.getSettablePaths({ global });
|
|
47870
|
+
const fileContent = await readFileContentOrNull(join(outputRoot, paths.relativeDirPath, paths.relativeFilePath)) ?? "{}";
|
|
47871
|
+
return new TabninePermissions({
|
|
47872
|
+
outputRoot,
|
|
47873
|
+
relativeDirPath: paths.relativeDirPath,
|
|
47874
|
+
relativeFilePath: paths.relativeFilePath,
|
|
47875
|
+
fileContent,
|
|
47876
|
+
validate,
|
|
47877
|
+
global
|
|
47878
|
+
});
|
|
47879
|
+
}
|
|
47880
|
+
static async fromRulesyncPermissions({ outputRoot = process.cwd(), rulesyncPermissions, global = false, logger }) {
|
|
47881
|
+
const paths = TabninePermissions.getSettablePaths({ global });
|
|
47882
|
+
const filePath = join(outputRoot, paths.relativeDirPath, paths.relativeFilePath);
|
|
47883
|
+
const existingContent = await readFileContentOrNull(filePath) ?? "{}";
|
|
47884
|
+
const config = rulesyncPermissions.getJson();
|
|
47885
|
+
const override = isRecord$1(config.tabnine) ? config.tabnine : {};
|
|
47886
|
+
const { tools: overrideTools, general: overrideGeneral, refused: refusedPaths } = stripRefusedOverridePaths(override);
|
|
47887
|
+
if (refusedPaths.length > 0) warnWithFallback(logger, `Tabnine CLI permissions: refused to write ${refusedPaths.join(", ")} from the tabnine override; Tabnine CLI runs that value as a command or sends its traffic to it, and a permissions file (one that came from 'rulesync fetch' included) must not be able to point it at an executable or a server of its choosing. Set it by hand in ${join(paths.relativeDirPath, paths.relativeFilePath)} if you need it.`);
|
|
47888
|
+
const overrideList = (key) => {
|
|
47889
|
+
const value = overrideTools[key];
|
|
47890
|
+
if (value === void 0 || isStringArray$2(value)) return value ?? [];
|
|
47891
|
+
warnWithFallback(logger, `Tabnine CLI permissions: ignored tools.${key} of the tabnine override (${quoteValueForWarning(value)}); it must be a list of tool names.`);
|
|
47892
|
+
return [];
|
|
47893
|
+
};
|
|
47894
|
+
const overrideShellAllowPatterns = [];
|
|
47895
|
+
const overrideVerbatimAllowed = [];
|
|
47896
|
+
for (const entry of overrideList(ALLOWED_KEY)) {
|
|
47897
|
+
const parsed = parseTabnineEntry(entry);
|
|
47898
|
+
if (parsed?.toolName === SHELL_TOOL_NAME) overrideShellAllowPatterns.push(toShellPattern(parsed.prefix));
|
|
47899
|
+
else overrideVerbatimAllowed.push(entry);
|
|
47900
|
+
}
|
|
47901
|
+
const overrideExclude = overrideList(EXCLUDE_KEY);
|
|
47902
|
+
const { allowed, exclude } = buildToolLists({
|
|
47903
|
+
permission: config.permission,
|
|
47904
|
+
overrideShellAllowPatterns,
|
|
47905
|
+
logger
|
|
47906
|
+
});
|
|
47907
|
+
const ignoredOverrideKeys = Object.keys(override).filter((key) => key !== TOOLS_KEY && key !== GENERAL_KEY);
|
|
47908
|
+
if (ignoredOverrideKeys.length > 0) warnWithFallback(logger, `Tabnine CLI permissions: ignored ${ignoredOverrideKeys.length} key(s) of the tabnine override (${ignoredOverrideKeys.map(quoteValueForWarning).join(", ")}); only 'tools' and 'general' are written through the permissions feature.`);
|
|
47909
|
+
const existingTools = this.existingToolsGroup(existingContent);
|
|
47910
|
+
const managedToolNames = new Set(Object.keys(config.permission).map((category) => category === "bash" || category === "*" ? SHELL_TOOL_NAME : toTabnineToolName(category)));
|
|
47911
|
+
const preservedEntries = (key) => (isStringArray$2(existingTools?.[key]) ? existingTools[key] : []).filter((entry) => {
|
|
47912
|
+
const toolName = parseTabnineEntry(entry)?.toolName;
|
|
47913
|
+
return toolName === void 0 || !managedToolNames.has(toolName);
|
|
47914
|
+
});
|
|
47915
|
+
const allowedList = uniq([
|
|
47916
|
+
...allowed,
|
|
47917
|
+
...overrideVerbatimAllowed,
|
|
47918
|
+
...preservedEntries(ALLOWED_KEY)
|
|
47919
|
+
]);
|
|
47920
|
+
const excludeList = uniq([
|
|
47921
|
+
...exclude,
|
|
47922
|
+
...overrideExclude,
|
|
47923
|
+
...preservedEntries(EXCLUDE_KEY)
|
|
47924
|
+
]);
|
|
47925
|
+
const droppedExcludeEntries = uniq(isStringArray$2(existingTools?.[EXCLUDE_KEY]) ? existingTools[EXCLUDE_KEY] : []).filter((entry) => {
|
|
47926
|
+
if (excludeList.includes(entry)) return false;
|
|
47927
|
+
const toolName = parseTabnineEntry(entry)?.toolName;
|
|
47928
|
+
return toolName === void 0 || !excludeList.includes(toolName);
|
|
47929
|
+
});
|
|
47930
|
+
if (droppedExcludeEntries.length > 0) warnWithFallback(logger, `Tabnine CLI permissions: removed ${droppedExcludeEntries.length} existing tools.exclude entry(ies) (${droppedExcludeEntries.map(quoteValueForWarning).join(", ")}) of a tool the canonical block manages; add a deny rule to .rulesync/permissions.jsonc to keep them.`);
|
|
47931
|
+
const tools = {
|
|
47932
|
+
...overrideTools,
|
|
47933
|
+
[ALLOWED_KEY]: allowedList.length > 0 ? allowedList : void 0,
|
|
47934
|
+
[EXCLUDE_KEY]: excludeList.length > 0 ? excludeList : void 0
|
|
47935
|
+
};
|
|
47936
|
+
const patch = {};
|
|
47937
|
+
if (overrideGeneral !== void 0) patch[GENERAL_KEY] = overrideGeneral;
|
|
47938
|
+
if (Object.values(tools).some((value) => value !== void 0) || existingTools !== void 0) patch[TOOLS_KEY] = tools;
|
|
47939
|
+
warnOnTrustAffectingEntries({
|
|
47940
|
+
toolLabel: "Tabnine CLI",
|
|
47941
|
+
entries: collectTrustAffectingOverrideEntries({
|
|
47942
|
+
tools: {
|
|
47943
|
+
...overrideTools,
|
|
47944
|
+
[ALLOWED_KEY]: overrideVerbatimAllowed
|
|
47945
|
+
},
|
|
47946
|
+
general: overrideGeneral
|
|
47947
|
+
}),
|
|
47948
|
+
relativeFilePath: join(paths.relativeDirPath, paths.relativeFilePath),
|
|
47949
|
+
logger
|
|
47950
|
+
});
|
|
47951
|
+
return new TabninePermissions({
|
|
47952
|
+
outputRoot,
|
|
47953
|
+
relativeDirPath: paths.relativeDirPath,
|
|
47954
|
+
relativeFilePath: paths.relativeFilePath,
|
|
47955
|
+
fileContent: applySharedConfigPatch({
|
|
47956
|
+
fileKey: sharedConfigFileKey(paths),
|
|
47957
|
+
feature: "permissions",
|
|
47958
|
+
existingContent,
|
|
47959
|
+
patch,
|
|
47960
|
+
filePath
|
|
47961
|
+
}),
|
|
47962
|
+
validate: true,
|
|
47963
|
+
global
|
|
47964
|
+
});
|
|
47965
|
+
}
|
|
47966
|
+
/** The `tools` group of the existing file, or `undefined` when it has none. */
|
|
47967
|
+
static existingToolsGroup(existingContent) {
|
|
47968
|
+
try {
|
|
47969
|
+
const existing = parseSharedConfig({
|
|
47970
|
+
format: "json",
|
|
47971
|
+
fileContent: existingContent || "{}",
|
|
47972
|
+
invalidRootPolicy: "error"
|
|
47973
|
+
});
|
|
47974
|
+
return isRecord$1(existing[TOOLS_KEY]) ? existing[TOOLS_KEY] : void 0;
|
|
47975
|
+
} catch {
|
|
47976
|
+
return;
|
|
47977
|
+
}
|
|
47978
|
+
}
|
|
47979
|
+
toRulesyncPermissions() {
|
|
47980
|
+
const settings = this.parseSettings();
|
|
47981
|
+
const tools = isRecord$1(settings[TOOLS_KEY]) ? settings[TOOLS_KEY] : {};
|
|
47982
|
+
const permission = {};
|
|
47983
|
+
const leftovers = {};
|
|
47984
|
+
const setRule = (category, pattern, action) => {
|
|
47985
|
+
const rules = Object.hasOwn(permission, category) ? permission[category] : permission[category] = {};
|
|
47986
|
+
if (rules !== void 0 && !Object.hasOwn(rules, pattern)) rules[pattern] = action;
|
|
47987
|
+
};
|
|
47988
|
+
for (const [key, action] of [[EXCLUDE_KEY, "deny"], [ALLOWED_KEY, "allow"]]) {
|
|
47989
|
+
const entries = isStringArray$2(tools[key]) ? tools[key] : [];
|
|
47990
|
+
for (const entry of entries) {
|
|
47991
|
+
const parsed = parseTabnineEntry(entry);
|
|
47992
|
+
if (parsed === void 0) {
|
|
47993
|
+
moduleLogger$1.warn(`Tabnine CLI permissions: kept malformed tools.${key} entry ${quoteValueForWarning(entry)} in the tabnine override.`);
|
|
47994
|
+
(leftovers[key] ??= []).push(entry);
|
|
47995
|
+
continue;
|
|
47996
|
+
}
|
|
47997
|
+
const { toolName, prefix } = parsed;
|
|
47998
|
+
if (toolName === SHELL_TOOL_NAME) {
|
|
47999
|
+
setRule(SHELL_PERMISSION_CATEGORY, toShellPattern(prefix), action);
|
|
48000
|
+
continue;
|
|
48001
|
+
}
|
|
48002
|
+
if (prefix !== void 0) {
|
|
48003
|
+
(leftovers[key] ??= []).push(entry);
|
|
48004
|
+
continue;
|
|
48005
|
+
}
|
|
48006
|
+
const category = lookupOwn({
|
|
48007
|
+
record: TABNINE_TO_CANONICAL_TOOL_NAMES,
|
|
48008
|
+
key: toolName
|
|
48009
|
+
}) ?? toolName;
|
|
48010
|
+
if (isPrototypePollutionKey(category)) {
|
|
48011
|
+
moduleLogger$1.warn(`Tabnine CLI permissions: kept tools.${key} entry ${quoteValueForWarning(entry)} in the tabnine override; its name cannot be a permission category.`);
|
|
48012
|
+
(leftovers[key] ??= []).push(entry);
|
|
48013
|
+
continue;
|
|
48014
|
+
}
|
|
48015
|
+
setRule(category, "*", action);
|
|
48016
|
+
}
|
|
48017
|
+
}
|
|
48018
|
+
const { tools: overrideTools, general, refused } = stripRefusedOverridePaths({
|
|
48019
|
+
[TOOLS_KEY]: Object.fromEntries(Object.entries(tools).filter(([key]) => key !== ALLOWED_KEY && key !== EXCLUDE_KEY)),
|
|
48020
|
+
[GENERAL_KEY]: settings[GENERAL_KEY]
|
|
48021
|
+
});
|
|
48022
|
+
for (const [key, entries] of Object.entries(leftovers)) overrideTools[key] = entries;
|
|
48023
|
+
const override = {};
|
|
48024
|
+
if (Object.keys(overrideTools).length > 0) override[TOOLS_KEY] = overrideTools;
|
|
48025
|
+
if (general !== void 0) override[GENERAL_KEY] = general;
|
|
48026
|
+
announceLiftedOverride({
|
|
48027
|
+
tools: overrideTools,
|
|
48028
|
+
general,
|
|
48029
|
+
refused
|
|
48030
|
+
});
|
|
48031
|
+
const imported = { permission };
|
|
48032
|
+
if (Object.keys(override).length > 0) imported.tabnine = override;
|
|
48033
|
+
return this.toRulesyncPermissionsDefault({ fileContent: JSON.stringify(imported, null, 2) });
|
|
48034
|
+
}
|
|
48035
|
+
parseSettings() {
|
|
48036
|
+
const relativePath = join(this.getRelativeDirPath(), this.getRelativeFilePath());
|
|
48037
|
+
try {
|
|
48038
|
+
return parseSharedConfig({
|
|
48039
|
+
format: "json",
|
|
48040
|
+
fileContent: this.getFileContent() || "{}",
|
|
48041
|
+
filePath: relativePath,
|
|
48042
|
+
invalidRootPolicy: "error"
|
|
48043
|
+
});
|
|
48044
|
+
} catch (error) {
|
|
48045
|
+
throw new Error(`Failed to parse Tabnine CLI settings in ${relativePath}: ${formatError(error)}`, { cause: error });
|
|
48046
|
+
}
|
|
48047
|
+
}
|
|
48048
|
+
validate() {
|
|
48049
|
+
return {
|
|
48050
|
+
success: true,
|
|
48051
|
+
error: null
|
|
48052
|
+
};
|
|
48053
|
+
}
|
|
48054
|
+
static forDeletion({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath, global = false }) {
|
|
48055
|
+
return new TabninePermissions({
|
|
48056
|
+
outputRoot,
|
|
48057
|
+
relativeDirPath,
|
|
48058
|
+
relativeFilePath,
|
|
48059
|
+
fileContent: "{}",
|
|
48060
|
+
validate: false,
|
|
48061
|
+
global
|
|
48062
|
+
});
|
|
48063
|
+
}
|
|
48064
|
+
};
|
|
48065
|
+
//#endregion
|
|
45552
48066
|
//#region src/features/permissions/takt-permissions.ts
|
|
45553
48067
|
const TAKT_PROVIDER_KEY = "provider";
|
|
45554
48068
|
const TAKT_PROVIDER_PROFILES_KEY = "provider_profiles";
|
|
@@ -48928,6 +51442,14 @@ const toolPermissionsFactories = /* @__PURE__ */ new Map([
|
|
|
48928
51442
|
supportsImport: true
|
|
48929
51443
|
}
|
|
48930
51444
|
}],
|
|
51445
|
+
["tabnine", {
|
|
51446
|
+
class: TabninePermissions,
|
|
51447
|
+
meta: {
|
|
51448
|
+
supportsProject: true,
|
|
51449
|
+
supportsGlobal: true,
|
|
51450
|
+
supportsImport: true
|
|
51451
|
+
}
|
|
51452
|
+
}],
|
|
48931
51453
|
["takt", {
|
|
48932
51454
|
class: TaktPermissions,
|
|
48933
51455
|
meta: {
|
|
@@ -50225,16 +52747,198 @@ var AntigravitySharedSkill = class extends ToolSkill {
|
|
|
50225
52747
|
}
|
|
50226
52748
|
static fromRulesyncSkill({ outputRoot = process.cwd(), rulesyncSkill, validate = true, global = false }) {
|
|
50227
52749
|
const rulesyncFrontmatter = rulesyncSkill.getFrontmatter();
|
|
50228
|
-
const antigravityFrontmatter = {
|
|
52750
|
+
const antigravityFrontmatter = {
|
|
52751
|
+
name: rulesyncFrontmatter.name,
|
|
52752
|
+
description: rulesyncFrontmatter.description
|
|
52753
|
+
};
|
|
52754
|
+
const settablePaths = this.getSettablePaths({ global });
|
|
52755
|
+
return new this({
|
|
52756
|
+
outputRoot,
|
|
52757
|
+
relativeDirPath: settablePaths.relativeDirPath,
|
|
52758
|
+
dirName: rulesyncSkill.getDirName(),
|
|
52759
|
+
frontmatter: antigravityFrontmatter,
|
|
52760
|
+
body: rulesyncSkill.getBody(),
|
|
52761
|
+
otherFiles: rulesyncSkill.getOtherFiles(),
|
|
52762
|
+
validate,
|
|
52763
|
+
global
|
|
52764
|
+
});
|
|
52765
|
+
}
|
|
52766
|
+
static isTargetedByRulesyncSkill(rulesyncSkill) {
|
|
52767
|
+
const targets = rulesyncSkill.getFrontmatter().targets;
|
|
52768
|
+
return targets.includes("*") || targets.includes(this.getToolTarget());
|
|
52769
|
+
}
|
|
52770
|
+
static async fromDir(params) {
|
|
52771
|
+
const loaded = await this.loadSkillDirContent({
|
|
52772
|
+
...params,
|
|
52773
|
+
getSettablePaths: (options) => this.getSettablePaths(options)
|
|
52774
|
+
});
|
|
52775
|
+
const result = AntigravitySkillFrontmatterSchema.safeParse(loaded.frontmatter);
|
|
52776
|
+
if (!result.success) {
|
|
52777
|
+
const skillDirPath = join(loaded.outputRoot, loaded.relativeDirPath, loaded.dirName);
|
|
52778
|
+
throw new Error(`Invalid frontmatter in ${join(skillDirPath, SKILL_FILE_NAME)}: ${formatError(result.error)}`);
|
|
52779
|
+
}
|
|
52780
|
+
return new this({
|
|
52781
|
+
outputRoot: loaded.outputRoot,
|
|
52782
|
+
relativeDirPath: loaded.relativeDirPath,
|
|
52783
|
+
dirName: loaded.dirName,
|
|
52784
|
+
frontmatter: result.data,
|
|
52785
|
+
body: loaded.body,
|
|
52786
|
+
otherFiles: loaded.otherFiles,
|
|
52787
|
+
validate: true,
|
|
52788
|
+
global: loaded.global
|
|
52789
|
+
});
|
|
52790
|
+
}
|
|
52791
|
+
static forDeletion({ outputRoot = process.cwd(), relativeDirPath, dirName, global = false }) {
|
|
52792
|
+
return new this({
|
|
52793
|
+
outputRoot,
|
|
52794
|
+
relativeDirPath,
|
|
52795
|
+
dirName,
|
|
52796
|
+
frontmatter: {
|
|
52797
|
+
name: "",
|
|
52798
|
+
description: ""
|
|
52799
|
+
},
|
|
52800
|
+
body: "",
|
|
52801
|
+
otherFiles: [],
|
|
52802
|
+
validate: false,
|
|
52803
|
+
global
|
|
52804
|
+
});
|
|
52805
|
+
}
|
|
52806
|
+
};
|
|
52807
|
+
//#endregion
|
|
52808
|
+
//#region src/features/skills/antigravity-cli-skill.ts
|
|
52809
|
+
/**
|
|
52810
|
+
* Represents a Google Antigravity CLI skill directory (Antigravity 2.0).
|
|
52811
|
+
*
|
|
52812
|
+
* Shares all behavior with {@link AntigravitySharedSkill}; the CLI keeps its
|
|
52813
|
+
* own global skills tree at `~/.gemini/antigravity-cli/skills/` and answers to
|
|
52814
|
+
* the `antigravity-cli` target.
|
|
52815
|
+
*/
|
|
52816
|
+
var AntigravityCliSkill = class extends AntigravitySharedSkill {
|
|
52817
|
+
static getGlobalSubdir() {
|
|
52818
|
+
return ANTIGRAVITY_CLI_PERMISSIONS_SUBDIR;
|
|
52819
|
+
}
|
|
52820
|
+
static getToolTarget() {
|
|
52821
|
+
return "antigravity-cli";
|
|
52822
|
+
}
|
|
52823
|
+
};
|
|
52824
|
+
//#endregion
|
|
52825
|
+
//#region src/features/skills/antigravity-ide-skill.ts
|
|
52826
|
+
/**
|
|
52827
|
+
* Represents a Google Antigravity IDE skill directory (Antigravity 2.0).
|
|
52828
|
+
*
|
|
52829
|
+
* Shares all behavior with {@link AntigravitySharedSkill}. As of Antigravity
|
|
52830
|
+
* 2.0 the IDE reads its global skills from the shared `~/.gemini/config/skills/`
|
|
52831
|
+
* location (the Skills doc lists `~/.gemini/config/skills/<skill-folder>/` as
|
|
52832
|
+
* the global scope); the legacy `~/.gemini/antigravity/skills/` path is no
|
|
52833
|
+
* longer used. It answers to the `antigravity-ide` target.
|
|
52834
|
+
*/
|
|
52835
|
+
var AntigravityIdeSkill = class extends AntigravitySharedSkill {
|
|
52836
|
+
static getGlobalSubdir() {
|
|
52837
|
+
return ANTIGRAVITY_IDE_GLOBAL_CONFIG_SUBDIR;
|
|
52838
|
+
}
|
|
52839
|
+
static getToolTarget() {
|
|
52840
|
+
return "antigravity-ide";
|
|
52841
|
+
}
|
|
52842
|
+
};
|
|
52843
|
+
//#endregion
|
|
52844
|
+
//#region src/features/skills/antigravity-plugin-skill.ts
|
|
52845
|
+
var AntigravityPluginSkill = class extends AntigravityIdeSkill {
|
|
52846
|
+
static isTargetedByRulesyncSkill(rulesyncSkill) {
|
|
52847
|
+
const targets = rulesyncSkill.getFrontmatter().targets;
|
|
52848
|
+
return targets.includes("*") || targets.includes("antigravity-plugin");
|
|
52849
|
+
}
|
|
52850
|
+
static getSettablePaths() {
|
|
52851
|
+
return { relativeDirPath: ANTIGRAVITY_PLUGIN_SKILLS_DIR };
|
|
52852
|
+
}
|
|
52853
|
+
};
|
|
52854
|
+
//#endregion
|
|
52855
|
+
//#region src/features/skills/augmentcode-skill.ts
|
|
52856
|
+
const AugmentcodeSkillFrontmatterSchema = z.looseObject({
|
|
52857
|
+
name: z.string(),
|
|
52858
|
+
description: z.string()
|
|
52859
|
+
});
|
|
52860
|
+
/**
|
|
52861
|
+
* Represents an AugmentCode (Auggie CLI) skill directory.
|
|
52862
|
+
* Auggie has native skill support — it reads `<name>/SKILL.md` directories under
|
|
52863
|
+
* `.augment/skills/` (project) and `~/.augment/skills/` (global).
|
|
52864
|
+
* See https://docs.augmentcode.com/cli/skills
|
|
52865
|
+
*/
|
|
52866
|
+
var AugmentcodeSkill = class AugmentcodeSkill extends ToolSkill {
|
|
52867
|
+
constructor({ outputRoot = process.cwd(), relativeDirPath = AugmentcodeSkill.getSettablePaths().relativeDirPath, dirName, frontmatter, body, otherFiles = [], validate = true, global = false }) {
|
|
52868
|
+
super({
|
|
52869
|
+
outputRoot,
|
|
52870
|
+
relativeDirPath,
|
|
52871
|
+
dirName,
|
|
52872
|
+
mainFile: {
|
|
52873
|
+
name: SKILL_FILE_NAME,
|
|
52874
|
+
body,
|
|
52875
|
+
frontmatter: { ...frontmatter }
|
|
52876
|
+
},
|
|
52877
|
+
otherFiles,
|
|
52878
|
+
global
|
|
52879
|
+
});
|
|
52880
|
+
if (validate) {
|
|
52881
|
+
const result = this.validate();
|
|
52882
|
+
if (!result.success) throw result.error;
|
|
52883
|
+
}
|
|
52884
|
+
}
|
|
52885
|
+
static getSettablePaths({ global: _global = false } = {}) {
|
|
52886
|
+
return {
|
|
52887
|
+
relativeDirPath: AUGMENTCODE_SKILLS_DIR_PATH,
|
|
52888
|
+
alternativeSkillRoots: [AUGMENTCODE_AGENTS_SKILLS_DIR_PATH]
|
|
52889
|
+
};
|
|
52890
|
+
}
|
|
52891
|
+
getFrontmatter() {
|
|
52892
|
+
return AugmentcodeSkillFrontmatterSchema.parse(this.requireMainFileFrontmatter());
|
|
52893
|
+
}
|
|
52894
|
+
getBody() {
|
|
52895
|
+
return this.mainFile?.body ?? "";
|
|
52896
|
+
}
|
|
52897
|
+
validate() {
|
|
52898
|
+
if (this.mainFile === void 0) return {
|
|
52899
|
+
success: false,
|
|
52900
|
+
error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME} file does not exist`)
|
|
52901
|
+
};
|
|
52902
|
+
const result = AugmentcodeSkillFrontmatterSchema.safeParse(this.mainFile.frontmatter);
|
|
52903
|
+
if (!result.success) return {
|
|
52904
|
+
success: false,
|
|
52905
|
+
error: /* @__PURE__ */ new Error(`Invalid frontmatter in ${this.getDirPath()}: ${formatError(result.error)}`)
|
|
52906
|
+
};
|
|
52907
|
+
return {
|
|
52908
|
+
success: true,
|
|
52909
|
+
error: null
|
|
52910
|
+
};
|
|
52911
|
+
}
|
|
52912
|
+
toRulesyncSkill() {
|
|
52913
|
+
const frontmatter = this.getFrontmatter();
|
|
52914
|
+
const rulesyncFrontmatter = {
|
|
52915
|
+
name: frontmatter.name,
|
|
52916
|
+
description: frontmatter.description,
|
|
52917
|
+
targets: ["*"]
|
|
52918
|
+
};
|
|
52919
|
+
return new RulesyncSkill({
|
|
52920
|
+
outputRoot: this.outputRoot,
|
|
52921
|
+
relativeDirPath: RULESYNC_SKILLS_RELATIVE_DIR_PATH,
|
|
52922
|
+
dirName: this.getDirName(),
|
|
52923
|
+
frontmatter: rulesyncFrontmatter,
|
|
52924
|
+
body: this.getBody(),
|
|
52925
|
+
otherFiles: this.getOtherFiles(),
|
|
52926
|
+
validate: true,
|
|
52927
|
+
global: this.global
|
|
52928
|
+
});
|
|
52929
|
+
}
|
|
52930
|
+
static fromRulesyncSkill({ outputRoot = process.cwd(), rulesyncSkill, validate = true, global = false }) {
|
|
52931
|
+
const settablePaths = AugmentcodeSkill.getSettablePaths({ global });
|
|
52932
|
+
const rulesyncFrontmatter = rulesyncSkill.getFrontmatter();
|
|
52933
|
+
const augmentcodeFrontmatter = {
|
|
50229
52934
|
name: rulesyncFrontmatter.name,
|
|
50230
52935
|
description: rulesyncFrontmatter.description
|
|
50231
52936
|
};
|
|
50232
|
-
|
|
50233
|
-
return new this({
|
|
52937
|
+
return new AugmentcodeSkill({
|
|
50234
52938
|
outputRoot,
|
|
50235
52939
|
relativeDirPath: settablePaths.relativeDirPath,
|
|
50236
52940
|
dirName: rulesyncSkill.getDirName(),
|
|
50237
|
-
frontmatter:
|
|
52941
|
+
frontmatter: augmentcodeFrontmatter,
|
|
50238
52942
|
body: rulesyncSkill.getBody(),
|
|
50239
52943
|
otherFiles: rulesyncSkill.getOtherFiles(),
|
|
50240
52944
|
validate,
|
|
@@ -50243,19 +52947,19 @@ var AntigravitySharedSkill = class extends ToolSkill {
|
|
|
50243
52947
|
}
|
|
50244
52948
|
static isTargetedByRulesyncSkill(rulesyncSkill) {
|
|
50245
52949
|
const targets = rulesyncSkill.getFrontmatter().targets;
|
|
50246
|
-
return targets.includes("*") || targets.includes(
|
|
52950
|
+
return targets.includes("*") || targets.includes("augmentcode");
|
|
50247
52951
|
}
|
|
50248
52952
|
static async fromDir(params) {
|
|
50249
52953
|
const loaded = await this.loadSkillDirContent({
|
|
50250
52954
|
...params,
|
|
50251
|
-
getSettablePaths:
|
|
52955
|
+
getSettablePaths: AugmentcodeSkill.getSettablePaths
|
|
50252
52956
|
});
|
|
50253
|
-
const result =
|
|
52957
|
+
const result = AugmentcodeSkillFrontmatterSchema.safeParse(loaded.frontmatter);
|
|
50254
52958
|
if (!result.success) {
|
|
50255
52959
|
const skillDirPath = join(loaded.outputRoot, loaded.relativeDirPath, loaded.dirName);
|
|
50256
52960
|
throw new Error(`Invalid frontmatter in ${join(skillDirPath, SKILL_FILE_NAME)}: ${formatError(result.error)}`);
|
|
50257
52961
|
}
|
|
50258
|
-
return new
|
|
52962
|
+
return new AugmentcodeSkill({
|
|
50259
52963
|
outputRoot: loaded.outputRoot,
|
|
50260
52964
|
relativeDirPath: loaded.relativeDirPath,
|
|
50261
52965
|
dirName: loaded.dirName,
|
|
@@ -50267,9 +52971,10 @@ var AntigravitySharedSkill = class extends ToolSkill {
|
|
|
50267
52971
|
});
|
|
50268
52972
|
}
|
|
50269
52973
|
static forDeletion({ outputRoot = process.cwd(), relativeDirPath, dirName, global = false }) {
|
|
50270
|
-
|
|
52974
|
+
const settablePaths = AugmentcodeSkill.getSettablePaths({ global });
|
|
52975
|
+
return new AugmentcodeSkill({
|
|
50271
52976
|
outputRoot,
|
|
50272
|
-
relativeDirPath,
|
|
52977
|
+
relativeDirPath: relativeDirPath ?? settablePaths.relativeDirPath,
|
|
50273
52978
|
dirName,
|
|
50274
52979
|
frontmatter: {
|
|
50275
52980
|
name: "",
|
|
@@ -50283,66 +52988,24 @@ var AntigravitySharedSkill = class extends ToolSkill {
|
|
|
50283
52988
|
}
|
|
50284
52989
|
};
|
|
50285
52990
|
//#endregion
|
|
50286
|
-
//#region src/features/skills/
|
|
50287
|
-
|
|
50288
|
-
* Represents a Google Antigravity CLI skill directory (Antigravity 2.0).
|
|
50289
|
-
*
|
|
50290
|
-
* Shares all behavior with {@link AntigravitySharedSkill}; the CLI keeps its
|
|
50291
|
-
* own global skills tree at `~/.gemini/antigravity-cli/skills/` and answers to
|
|
50292
|
-
* the `antigravity-cli` target.
|
|
50293
|
-
*/
|
|
50294
|
-
var AntigravityCliSkill = class extends AntigravitySharedSkill {
|
|
50295
|
-
static getGlobalSubdir() {
|
|
50296
|
-
return ANTIGRAVITY_CLI_PERMISSIONS_SUBDIR;
|
|
50297
|
-
}
|
|
50298
|
-
static getToolTarget() {
|
|
50299
|
-
return "antigravity-cli";
|
|
50300
|
-
}
|
|
50301
|
-
};
|
|
50302
|
-
//#endregion
|
|
50303
|
-
//#region src/features/skills/antigravity-ide-skill.ts
|
|
50304
|
-
/**
|
|
50305
|
-
* Represents a Google Antigravity IDE skill directory (Antigravity 2.0).
|
|
50306
|
-
*
|
|
50307
|
-
* Shares all behavior with {@link AntigravitySharedSkill}. As of Antigravity
|
|
50308
|
-
* 2.0 the IDE reads its global skills from the shared `~/.gemini/config/skills/`
|
|
50309
|
-
* location (the Skills doc lists `~/.gemini/config/skills/<skill-folder>/` as
|
|
50310
|
-
* the global scope); the legacy `~/.gemini/antigravity/skills/` path is no
|
|
50311
|
-
* longer used. It answers to the `antigravity-ide` target.
|
|
50312
|
-
*/
|
|
50313
|
-
var AntigravityIdeSkill = class extends AntigravitySharedSkill {
|
|
50314
|
-
static getGlobalSubdir() {
|
|
50315
|
-
return ANTIGRAVITY_IDE_GLOBAL_CONFIG_SUBDIR;
|
|
50316
|
-
}
|
|
50317
|
-
static getToolTarget() {
|
|
50318
|
-
return "antigravity-ide";
|
|
50319
|
-
}
|
|
50320
|
-
};
|
|
50321
|
-
//#endregion
|
|
50322
|
-
//#region src/features/skills/antigravity-plugin-skill.ts
|
|
50323
|
-
var AntigravityPluginSkill = class extends AntigravityIdeSkill {
|
|
50324
|
-
static isTargetedByRulesyncSkill(rulesyncSkill) {
|
|
50325
|
-
const targets = rulesyncSkill.getFrontmatter().targets;
|
|
50326
|
-
return targets.includes("*") || targets.includes("antigravity-plugin");
|
|
50327
|
-
}
|
|
50328
|
-
static getSettablePaths() {
|
|
50329
|
-
return { relativeDirPath: ANTIGRAVITY_PLUGIN_SKILLS_DIR };
|
|
50330
|
-
}
|
|
50331
|
-
};
|
|
50332
|
-
//#endregion
|
|
50333
|
-
//#region src/features/skills/augmentcode-skill.ts
|
|
50334
|
-
const AugmentcodeSkillFrontmatterSchema = z.looseObject({
|
|
52991
|
+
//#region src/features/skills/bob-skill.ts
|
|
52992
|
+
const BobSkillFrontmatterSchema = z.looseObject({
|
|
50335
52993
|
name: z.string(),
|
|
50336
52994
|
description: z.string()
|
|
50337
52995
|
});
|
|
50338
52996
|
/**
|
|
50339
|
-
* Represents an
|
|
50340
|
-
*
|
|
50341
|
-
*
|
|
50342
|
-
*
|
|
52997
|
+
* Represents an IBM Bob skill directory.
|
|
52998
|
+
*
|
|
52999
|
+
* Bob discovers Anthropic-style `<name>/SKILL.md` directories under
|
|
53000
|
+
* `<project>/.bob/skills/` (project scope) and `~/.bob/skills/` (user scope);
|
|
53001
|
+
* the project copy wins on a name clash. The frontmatter requires `name`
|
|
53002
|
+
* (matching the directory name) and `description`; supporting files next to
|
|
53003
|
+
* `SKILL.md` are carried along.
|
|
53004
|
+
*
|
|
53005
|
+
* @see https://bob.ibm.com/docs/ide/features/skills
|
|
50343
53006
|
*/
|
|
50344
|
-
var
|
|
50345
|
-
constructor({ outputRoot = process.cwd(), relativeDirPath =
|
|
53007
|
+
var BobSkill = class BobSkill extends ToolSkill {
|
|
53008
|
+
constructor({ outputRoot = process.cwd(), relativeDirPath = BOB_SKILLS_DIR_PATH, dirName, frontmatter, body, otherFiles = [], validate = true, global = false }) {
|
|
50346
53009
|
super({
|
|
50347
53010
|
outputRoot,
|
|
50348
53011
|
relativeDirPath,
|
|
@@ -50361,38 +53024,40 @@ var AugmentcodeSkill = class AugmentcodeSkill extends ToolSkill {
|
|
|
50361
53024
|
}
|
|
50362
53025
|
}
|
|
50363
53026
|
static getSettablePaths({ global: _global = false } = {}) {
|
|
50364
|
-
return {
|
|
50365
|
-
relativeDirPath: AUGMENTCODE_SKILLS_DIR_PATH,
|
|
50366
|
-
alternativeSkillRoots: [AUGMENTCODE_AGENTS_SKILLS_DIR_PATH]
|
|
50367
|
-
};
|
|
53027
|
+
return { relativeDirPath: BOB_SKILLS_DIR_PATH };
|
|
50368
53028
|
}
|
|
50369
53029
|
getFrontmatter() {
|
|
50370
|
-
return
|
|
53030
|
+
return BobSkillFrontmatterSchema.parse(this.requireMainFileFrontmatter());
|
|
50371
53031
|
}
|
|
50372
53032
|
getBody() {
|
|
50373
53033
|
return this.mainFile?.body ?? "";
|
|
50374
53034
|
}
|
|
50375
53035
|
validate() {
|
|
50376
|
-
if (this.mainFile
|
|
53036
|
+
if (!this.mainFile) return {
|
|
50377
53037
|
success: false,
|
|
50378
53038
|
error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME} file does not exist`)
|
|
50379
53039
|
};
|
|
50380
|
-
const result =
|
|
53040
|
+
const result = BobSkillFrontmatterSchema.safeParse(this.mainFile.frontmatter);
|
|
50381
53041
|
if (!result.success) return {
|
|
50382
53042
|
success: false,
|
|
50383
53043
|
error: /* @__PURE__ */ new Error(`Invalid frontmatter in ${this.getDirPath()}: ${formatError(result.error)}`)
|
|
50384
53044
|
};
|
|
53045
|
+
if (result.data.name !== this.getDirName()) return {
|
|
53046
|
+
success: false,
|
|
53047
|
+
error: /* @__PURE__ */ new Error(`${this.getDirPath()}: frontmatter name (${result.data.name}) must match directory name (${this.getDirName()})`)
|
|
53048
|
+
};
|
|
50385
53049
|
return {
|
|
50386
53050
|
success: true,
|
|
50387
53051
|
error: null
|
|
50388
53052
|
};
|
|
50389
53053
|
}
|
|
50390
53054
|
toRulesyncSkill() {
|
|
50391
|
-
const
|
|
53055
|
+
const { name, description, ...bobSection } = this.getFrontmatter();
|
|
50392
53056
|
const rulesyncFrontmatter = {
|
|
50393
|
-
name
|
|
50394
|
-
description
|
|
50395
|
-
targets: ["*"]
|
|
53057
|
+
name,
|
|
53058
|
+
description,
|
|
53059
|
+
targets: ["*"],
|
|
53060
|
+
...Object.keys(bobSection).length > 0 && { bob: bobSection }
|
|
50396
53061
|
};
|
|
50397
53062
|
return new RulesyncSkill({
|
|
50398
53063
|
outputRoot: this.outputRoot,
|
|
@@ -50406,17 +53071,19 @@ var AugmentcodeSkill = class AugmentcodeSkill extends ToolSkill {
|
|
|
50406
53071
|
});
|
|
50407
53072
|
}
|
|
50408
53073
|
static fromRulesyncSkill({ outputRoot = process.cwd(), rulesyncSkill, validate = true, global = false }) {
|
|
50409
|
-
const settablePaths =
|
|
53074
|
+
const settablePaths = BobSkill.getSettablePaths({ global });
|
|
50410
53075
|
const rulesyncFrontmatter = rulesyncSkill.getFrontmatter();
|
|
50411
|
-
const
|
|
53076
|
+
const { name: _sectionName, description: _sectionDescription, ...bobSection } = rulesyncFrontmatter.bob ?? {};
|
|
53077
|
+
const bobFrontmatter = {
|
|
53078
|
+
...bobSection,
|
|
50412
53079
|
name: rulesyncFrontmatter.name,
|
|
50413
53080
|
description: rulesyncFrontmatter.description
|
|
50414
53081
|
};
|
|
50415
|
-
return new
|
|
53082
|
+
return new BobSkill({
|
|
50416
53083
|
outputRoot,
|
|
50417
53084
|
relativeDirPath: settablePaths.relativeDirPath,
|
|
50418
|
-
dirName:
|
|
50419
|
-
frontmatter:
|
|
53085
|
+
dirName: bobFrontmatter.name,
|
|
53086
|
+
frontmatter: bobFrontmatter,
|
|
50420
53087
|
body: rulesyncSkill.getBody(),
|
|
50421
53088
|
otherFiles: rulesyncSkill.getOtherFiles(),
|
|
50422
53089
|
validate,
|
|
@@ -50425,19 +53092,23 @@ var AugmentcodeSkill = class AugmentcodeSkill extends ToolSkill {
|
|
|
50425
53092
|
}
|
|
50426
53093
|
static isTargetedByRulesyncSkill(rulesyncSkill) {
|
|
50427
53094
|
const targets = rulesyncSkill.getFrontmatter().targets;
|
|
50428
|
-
return targets.includes("*") || targets.includes("
|
|
53095
|
+
return targets.includes("*") || targets.includes("bob");
|
|
50429
53096
|
}
|
|
50430
53097
|
static async fromDir(params) {
|
|
50431
53098
|
const loaded = await this.loadSkillDirContent({
|
|
50432
53099
|
...params,
|
|
50433
|
-
getSettablePaths:
|
|
53100
|
+
getSettablePaths: BobSkill.getSettablePaths
|
|
50434
53101
|
});
|
|
50435
|
-
const result =
|
|
53102
|
+
const result = BobSkillFrontmatterSchema.safeParse(loaded.frontmatter);
|
|
50436
53103
|
if (!result.success) {
|
|
50437
53104
|
const skillDirPath = join(loaded.outputRoot, loaded.relativeDirPath, loaded.dirName);
|
|
50438
53105
|
throw new Error(`Invalid frontmatter in ${join(skillDirPath, SKILL_FILE_NAME)}: ${formatError(result.error)}`);
|
|
50439
53106
|
}
|
|
50440
|
-
|
|
53107
|
+
if (result.data.name !== loaded.dirName) {
|
|
53108
|
+
const skillFilePath = join(loaded.outputRoot, loaded.relativeDirPath, loaded.dirName, SKILL_FILE_NAME);
|
|
53109
|
+
throw new Error(`Frontmatter name (${result.data.name}) must match directory name (${loaded.dirName}) in ${skillFilePath}`);
|
|
53110
|
+
}
|
|
53111
|
+
return new BobSkill({
|
|
50441
53112
|
outputRoot: loaded.outputRoot,
|
|
50442
53113
|
relativeDirPath: loaded.relativeDirPath,
|
|
50443
53114
|
dirName: loaded.dirName,
|
|
@@ -50449,10 +53120,9 @@ var AugmentcodeSkill = class AugmentcodeSkill extends ToolSkill {
|
|
|
50449
53120
|
});
|
|
50450
53121
|
}
|
|
50451
53122
|
static forDeletion({ outputRoot = process.cwd(), relativeDirPath, dirName, global = false }) {
|
|
50452
|
-
|
|
50453
|
-
return new AugmentcodeSkill({
|
|
53123
|
+
return new BobSkill({
|
|
50454
53124
|
outputRoot,
|
|
50455
|
-
relativeDirPath
|
|
53125
|
+
relativeDirPath,
|
|
50456
53126
|
dirName,
|
|
50457
53127
|
frontmatter: {
|
|
50458
53128
|
name: "",
|
|
@@ -51397,7 +54067,167 @@ var CopilotSkill = class CopilotSkill extends ToolSkill {
|
|
|
51397
54067
|
return { relativeDirPath: COPILOT_SKILLS_DIR_PATH };
|
|
51398
54068
|
}
|
|
51399
54069
|
getFrontmatter() {
|
|
51400
|
-
return CopilotSkillFrontmatterSchema.parse(this.requireMainFileFrontmatter());
|
|
54070
|
+
return CopilotSkillFrontmatterSchema.parse(this.requireMainFileFrontmatter());
|
|
54071
|
+
}
|
|
54072
|
+
getBody() {
|
|
54073
|
+
return this.mainFile?.body ?? "";
|
|
54074
|
+
}
|
|
54075
|
+
validate() {
|
|
54076
|
+
if (!this.mainFile) return {
|
|
54077
|
+
success: false,
|
|
54078
|
+
error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME} file does not exist`)
|
|
54079
|
+
};
|
|
54080
|
+
const result = CopilotSkillFrontmatterSchema.safeParse(this.mainFile.frontmatter);
|
|
54081
|
+
if (!result.success) return {
|
|
54082
|
+
success: false,
|
|
54083
|
+
error: /* @__PURE__ */ new Error(`Invalid frontmatter in ${this.getDirPath()}: ${formatError(result.error)}`)
|
|
54084
|
+
};
|
|
54085
|
+
return {
|
|
54086
|
+
success: true,
|
|
54087
|
+
error: null
|
|
54088
|
+
};
|
|
54089
|
+
}
|
|
54090
|
+
toRulesyncSkill() {
|
|
54091
|
+
const { name, description, ...copilotSection } = this.getFrontmatter();
|
|
54092
|
+
const rulesyncFrontmatter = {
|
|
54093
|
+
name,
|
|
54094
|
+
description,
|
|
54095
|
+
targets: ["*"],
|
|
54096
|
+
...Object.keys(copilotSection).length > 0 && { copilot: copilotSection }
|
|
54097
|
+
};
|
|
54098
|
+
return new RulesyncSkill({
|
|
54099
|
+
outputRoot: this.outputRoot,
|
|
54100
|
+
relativeDirPath: RULESYNC_SKILLS_RELATIVE_DIR_PATH,
|
|
54101
|
+
dirName: this.getDirName(),
|
|
54102
|
+
frontmatter: rulesyncFrontmatter,
|
|
54103
|
+
body: this.getBody(),
|
|
54104
|
+
otherFiles: this.getOtherFiles(),
|
|
54105
|
+
validate: true,
|
|
54106
|
+
global: this.global
|
|
54107
|
+
});
|
|
54108
|
+
}
|
|
54109
|
+
static fromRulesyncSkill({ outputRoot = process.cwd(), rulesyncSkill, validate = true, global = false }) {
|
|
54110
|
+
const settablePaths = CopilotSkill.getSettablePaths({ global });
|
|
54111
|
+
const rulesyncFrontmatter = rulesyncSkill.getFrontmatter();
|
|
54112
|
+
const copilotSection = rulesyncFrontmatter.copilot;
|
|
54113
|
+
const resolvedUserInvocable = resolveUserInvocable({
|
|
54114
|
+
rootFrontmatter: rulesyncFrontmatter,
|
|
54115
|
+
section: copilotSection
|
|
54116
|
+
});
|
|
54117
|
+
const resolvedDisableModelInvocation = resolveDisableModelInvocation({
|
|
54118
|
+
rootFrontmatter: rulesyncFrontmatter,
|
|
54119
|
+
section: copilotSection
|
|
54120
|
+
});
|
|
54121
|
+
const license = resolveLicense({
|
|
54122
|
+
rootFrontmatter: rulesyncFrontmatter,
|
|
54123
|
+
section: copilotSection
|
|
54124
|
+
});
|
|
54125
|
+
const { "user-invocable": _userInvocable, "disable-model-invocation": _disableModelInvocation, license: _license, ...copilotFields } = copilotSection ?? {};
|
|
54126
|
+
const copilotFrontmatter = {
|
|
54127
|
+
...copilotFields,
|
|
54128
|
+
name: rulesyncFrontmatter.name,
|
|
54129
|
+
description: rulesyncFrontmatter.description,
|
|
54130
|
+
...license !== void 0 && { license },
|
|
54131
|
+
...resolvedUserInvocable !== void 0 && { "user-invocable": resolvedUserInvocable },
|
|
54132
|
+
...resolvedDisableModelInvocation !== void 0 && { "disable-model-invocation": resolvedDisableModelInvocation }
|
|
54133
|
+
};
|
|
54134
|
+
return new CopilotSkill({
|
|
54135
|
+
outputRoot,
|
|
54136
|
+
relativeDirPath: settablePaths.relativeDirPath,
|
|
54137
|
+
dirName: rulesyncSkill.getDirName(),
|
|
54138
|
+
frontmatter: copilotFrontmatter,
|
|
54139
|
+
body: rulesyncSkill.getBody(),
|
|
54140
|
+
otherFiles: rulesyncSkill.getOtherFiles(),
|
|
54141
|
+
validate,
|
|
54142
|
+
global
|
|
54143
|
+
});
|
|
54144
|
+
}
|
|
54145
|
+
static isTargetedByRulesyncSkill(rulesyncSkill) {
|
|
54146
|
+
const targets = rulesyncSkill.getFrontmatter().targets;
|
|
54147
|
+
return targets.includes("*") || targets.includes("copilot");
|
|
54148
|
+
}
|
|
54149
|
+
static async fromDir(params) {
|
|
54150
|
+
const loaded = await this.loadSkillDirContent({
|
|
54151
|
+
...params,
|
|
54152
|
+
getSettablePaths: CopilotSkill.getSettablePaths
|
|
54153
|
+
});
|
|
54154
|
+
const result = CopilotSkillFrontmatterSchema.safeParse(loaded.frontmatter);
|
|
54155
|
+
if (!result.success) {
|
|
54156
|
+
const skillDirPath = join(loaded.outputRoot, loaded.relativeDirPath, loaded.dirName);
|
|
54157
|
+
throw new Error(`Invalid frontmatter in ${join(skillDirPath, SKILL_FILE_NAME)}: ${formatError(result.error)}`);
|
|
54158
|
+
}
|
|
54159
|
+
return new CopilotSkill({
|
|
54160
|
+
outputRoot: loaded.outputRoot,
|
|
54161
|
+
relativeDirPath: loaded.relativeDirPath,
|
|
54162
|
+
dirName: loaded.dirName,
|
|
54163
|
+
frontmatter: result.data,
|
|
54164
|
+
body: loaded.body,
|
|
54165
|
+
otherFiles: loaded.otherFiles,
|
|
54166
|
+
validate: true,
|
|
54167
|
+
global: loaded.global
|
|
54168
|
+
});
|
|
54169
|
+
}
|
|
54170
|
+
static forDeletion({ outputRoot = process.cwd(), relativeDirPath, dirName, global = false }) {
|
|
54171
|
+
const settablePaths = CopilotSkill.getSettablePaths({ global });
|
|
54172
|
+
return new CopilotSkill({
|
|
54173
|
+
outputRoot,
|
|
54174
|
+
relativeDirPath: relativeDirPath ?? settablePaths.relativeDirPath,
|
|
54175
|
+
dirName,
|
|
54176
|
+
frontmatter: {
|
|
54177
|
+
name: "",
|
|
54178
|
+
description: ""
|
|
54179
|
+
},
|
|
54180
|
+
body: "",
|
|
54181
|
+
otherFiles: [],
|
|
54182
|
+
validate: false,
|
|
54183
|
+
global
|
|
54184
|
+
});
|
|
54185
|
+
}
|
|
54186
|
+
};
|
|
54187
|
+
//#endregion
|
|
54188
|
+
//#region src/features/skills/copilotcli-skill.ts
|
|
54189
|
+
const CopilotcliSkillFrontmatterSchema = z.looseObject({
|
|
54190
|
+
name: z.string(),
|
|
54191
|
+
description: z.string(),
|
|
54192
|
+
license: z.optional(z.string()),
|
|
54193
|
+
"allowed-tools": z.optional(z.union([z.string(), z.array(z.string())])),
|
|
54194
|
+
"argument-hint": z.optional(z.string()),
|
|
54195
|
+
"user-invocable": z.optional(z.boolean()),
|
|
54196
|
+
"disable-model-invocation": z.optional(z.boolean())
|
|
54197
|
+
});
|
|
54198
|
+
/**
|
|
54199
|
+
* Represents a GitHub Copilot CLI skill directory.
|
|
54200
|
+
*
|
|
54201
|
+
* Copilot CLI discovers project skills from `.github/skills/` (shared with the
|
|
54202
|
+
* Copilot IDE target) and personal/global skills from `~/.copilot/skills/`. Each
|
|
54203
|
+
* skill is a directory containing a `SKILL.md` file with `name`/`description`
|
|
54204
|
+
* frontmatter. https://docs.github.com/en/copilot/how-tos/copilot-cli/customize-copilot/add-skills
|
|
54205
|
+
*/
|
|
54206
|
+
var CopilotcliSkill = class CopilotcliSkill extends ToolSkill {
|
|
54207
|
+
constructor({ outputRoot = process.cwd(), relativeDirPath = COPILOT_SKILLS_DIR_PATH, dirName, frontmatter, body, otherFiles = [], validate = true, global = false }) {
|
|
54208
|
+
super({
|
|
54209
|
+
outputRoot,
|
|
54210
|
+
relativeDirPath,
|
|
54211
|
+
dirName,
|
|
54212
|
+
mainFile: {
|
|
54213
|
+
name: SKILL_FILE_NAME,
|
|
54214
|
+
body,
|
|
54215
|
+
frontmatter: { ...frontmatter }
|
|
54216
|
+
},
|
|
54217
|
+
otherFiles,
|
|
54218
|
+
global
|
|
54219
|
+
});
|
|
54220
|
+
if (validate) {
|
|
54221
|
+
const result = this.validate();
|
|
54222
|
+
if (!result.success) throw result.error;
|
|
54223
|
+
}
|
|
54224
|
+
}
|
|
54225
|
+
static getSettablePaths(options) {
|
|
54226
|
+
if (options?.global) return { relativeDirPath: COPILOT_SKILLS_GLOBAL_DIR_PATH };
|
|
54227
|
+
return { relativeDirPath: COPILOT_SKILLS_DIR_PATH };
|
|
54228
|
+
}
|
|
54229
|
+
getFrontmatter() {
|
|
54230
|
+
return CopilotcliSkillFrontmatterSchema.parse(this.requireMainFileFrontmatter());
|
|
51401
54231
|
}
|
|
51402
54232
|
getBody() {
|
|
51403
54233
|
return this.mainFile?.body ?? "";
|
|
@@ -51407,7 +54237,7 @@ var CopilotSkill = class CopilotSkill extends ToolSkill {
|
|
|
51407
54237
|
success: false,
|
|
51408
54238
|
error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME} file does not exist`)
|
|
51409
54239
|
};
|
|
51410
|
-
const result =
|
|
54240
|
+
const result = CopilotcliSkillFrontmatterSchema.safeParse(this.mainFile.frontmatter);
|
|
51411
54241
|
if (!result.success) return {
|
|
51412
54242
|
success: false,
|
|
51413
54243
|
error: /* @__PURE__ */ new Error(`Invalid frontmatter in ${this.getDirPath()}: ${formatError(result.error)}`)
|
|
@@ -51418,12 +54248,12 @@ var CopilotSkill = class CopilotSkill extends ToolSkill {
|
|
|
51418
54248
|
};
|
|
51419
54249
|
}
|
|
51420
54250
|
toRulesyncSkill() {
|
|
51421
|
-
const { name, description, ...
|
|
54251
|
+
const { name, description, ...copilotcliSection } = this.getFrontmatter();
|
|
51422
54252
|
const rulesyncFrontmatter = {
|
|
51423
54253
|
name,
|
|
51424
54254
|
description,
|
|
51425
54255
|
targets: ["*"],
|
|
51426
|
-
...Object.keys(
|
|
54256
|
+
...Object.keys(copilotcliSection).length > 0 && { copilotcli: copilotcliSection }
|
|
51427
54257
|
};
|
|
51428
54258
|
return new RulesyncSkill({
|
|
51429
54259
|
outputRoot: this.outputRoot,
|
|
@@ -51437,35 +54267,35 @@ var CopilotSkill = class CopilotSkill extends ToolSkill {
|
|
|
51437
54267
|
});
|
|
51438
54268
|
}
|
|
51439
54269
|
static fromRulesyncSkill({ outputRoot = process.cwd(), rulesyncSkill, validate = true, global = false }) {
|
|
51440
|
-
const settablePaths =
|
|
54270
|
+
const settablePaths = CopilotcliSkill.getSettablePaths({ global });
|
|
51441
54271
|
const rulesyncFrontmatter = rulesyncSkill.getFrontmatter();
|
|
51442
|
-
const
|
|
54272
|
+
const copilotcliSection = rulesyncFrontmatter.copilotcli;
|
|
51443
54273
|
const resolvedUserInvocable = resolveUserInvocable({
|
|
51444
54274
|
rootFrontmatter: rulesyncFrontmatter,
|
|
51445
|
-
section:
|
|
54275
|
+
section: copilotcliSection
|
|
51446
54276
|
});
|
|
51447
54277
|
const resolvedDisableModelInvocation = resolveDisableModelInvocation({
|
|
51448
54278
|
rootFrontmatter: rulesyncFrontmatter,
|
|
51449
|
-
section:
|
|
54279
|
+
section: copilotcliSection
|
|
51450
54280
|
});
|
|
51451
54281
|
const license = resolveLicense({
|
|
51452
54282
|
rootFrontmatter: rulesyncFrontmatter,
|
|
51453
|
-
section:
|
|
54283
|
+
section: copilotcliSection
|
|
51454
54284
|
});
|
|
51455
|
-
const { "user-invocable": _userInvocable, "disable-model-invocation": _disableModelInvocation, license: _license, ...
|
|
51456
|
-
const
|
|
51457
|
-
...
|
|
54285
|
+
const { "user-invocable": _userInvocable, "disable-model-invocation": _disableModelInvocation, license: _license, ...copilotcliFields } = copilotcliSection ?? {};
|
|
54286
|
+
const copilotcliFrontmatter = {
|
|
54287
|
+
...copilotcliFields,
|
|
51458
54288
|
name: rulesyncFrontmatter.name,
|
|
51459
54289
|
description: rulesyncFrontmatter.description,
|
|
51460
54290
|
...license !== void 0 && { license },
|
|
51461
54291
|
...resolvedUserInvocable !== void 0 && { "user-invocable": resolvedUserInvocable },
|
|
51462
54292
|
...resolvedDisableModelInvocation !== void 0 && { "disable-model-invocation": resolvedDisableModelInvocation }
|
|
51463
54293
|
};
|
|
51464
|
-
return new
|
|
54294
|
+
return new CopilotcliSkill({
|
|
51465
54295
|
outputRoot,
|
|
51466
54296
|
relativeDirPath: settablePaths.relativeDirPath,
|
|
51467
54297
|
dirName: rulesyncSkill.getDirName(),
|
|
51468
|
-
frontmatter:
|
|
54298
|
+
frontmatter: copilotcliFrontmatter,
|
|
51469
54299
|
body: rulesyncSkill.getBody(),
|
|
51470
54300
|
otherFiles: rulesyncSkill.getOtherFiles(),
|
|
51471
54301
|
validate,
|
|
@@ -51474,19 +54304,19 @@ var CopilotSkill = class CopilotSkill extends ToolSkill {
|
|
|
51474
54304
|
}
|
|
51475
54305
|
static isTargetedByRulesyncSkill(rulesyncSkill) {
|
|
51476
54306
|
const targets = rulesyncSkill.getFrontmatter().targets;
|
|
51477
|
-
return targets.includes("*") || targets.includes("
|
|
54307
|
+
return targets.includes("*") || targets.includes("copilotcli");
|
|
51478
54308
|
}
|
|
51479
54309
|
static async fromDir(params) {
|
|
51480
54310
|
const loaded = await this.loadSkillDirContent({
|
|
51481
54311
|
...params,
|
|
51482
|
-
getSettablePaths:
|
|
54312
|
+
getSettablePaths: CopilotcliSkill.getSettablePaths
|
|
51483
54313
|
});
|
|
51484
|
-
const result =
|
|
54314
|
+
const result = CopilotcliSkillFrontmatterSchema.safeParse(loaded.frontmatter);
|
|
51485
54315
|
if (!result.success) {
|
|
51486
54316
|
const skillDirPath = join(loaded.outputRoot, loaded.relativeDirPath, loaded.dirName);
|
|
51487
54317
|
throw new Error(`Invalid frontmatter in ${join(skillDirPath, SKILL_FILE_NAME)}: ${formatError(result.error)}`);
|
|
51488
54318
|
}
|
|
51489
|
-
return new
|
|
54319
|
+
return new CopilotcliSkill({
|
|
51490
54320
|
outputRoot: loaded.outputRoot,
|
|
51491
54321
|
relativeDirPath: loaded.relativeDirPath,
|
|
51492
54322
|
dirName: loaded.dirName,
|
|
@@ -51498,8 +54328,8 @@ var CopilotSkill = class CopilotSkill extends ToolSkill {
|
|
|
51498
54328
|
});
|
|
51499
54329
|
}
|
|
51500
54330
|
static forDeletion({ outputRoot = process.cwd(), relativeDirPath, dirName, global = false }) {
|
|
51501
|
-
const settablePaths =
|
|
51502
|
-
return new
|
|
54331
|
+
const settablePaths = CopilotcliSkill.getSettablePaths({ global });
|
|
54332
|
+
return new CopilotcliSkill({
|
|
51503
54333
|
outputRoot,
|
|
51504
54334
|
relativeDirPath: relativeDirPath ?? settablePaths.relativeDirPath,
|
|
51505
54335
|
dirName,
|
|
@@ -51515,26 +54345,25 @@ var CopilotSkill = class CopilotSkill extends ToolSkill {
|
|
|
51515
54345
|
}
|
|
51516
54346
|
};
|
|
51517
54347
|
//#endregion
|
|
51518
|
-
//#region src/features/skills/
|
|
51519
|
-
const
|
|
54348
|
+
//#region src/features/skills/cortexcode-skill.ts
|
|
54349
|
+
const CortexcodeSkillFrontmatterSchema = z.looseObject({
|
|
51520
54350
|
name: z.string(),
|
|
51521
54351
|
description: z.string(),
|
|
51522
|
-
|
|
51523
|
-
|
|
51524
|
-
"argument-hint": z.optional(z.string()),
|
|
51525
|
-
"user-invocable": z.optional(z.boolean()),
|
|
51526
|
-
"disable-model-invocation": z.optional(z.boolean())
|
|
54352
|
+
/** Tool names the skill may use; omitted = every tool. */
|
|
54353
|
+
tools: z.optional(z.array(z.string()))
|
|
51527
54354
|
});
|
|
51528
54355
|
/**
|
|
51529
|
-
* Represents a
|
|
54356
|
+
* Represents a Snowflake Cortex Code skill directory.
|
|
51530
54357
|
*
|
|
51531
|
-
*
|
|
51532
|
-
*
|
|
51533
|
-
*
|
|
51534
|
-
*
|
|
54358
|
+
* Cortex Code discovers Anthropic-style `<name>/SKILL.md` directories under
|
|
54359
|
+
* `<project>/.cortex/skills/` (project scope) and `~/.snowflake/cortex/skills/`
|
|
54360
|
+
* (user scope). The frontmatter requires `name` (matching the directory name)
|
|
54361
|
+
* and `description`; supporting files next to `SKILL.md` are carried along.
|
|
54362
|
+
*
|
|
54363
|
+
* @see https://docs.snowflake.com/en/user-guide/cortex-code/extensibility
|
|
51535
54364
|
*/
|
|
51536
|
-
var
|
|
51537
|
-
constructor({ outputRoot = process.cwd(), relativeDirPath =
|
|
54365
|
+
var CortexcodeSkill = class CortexcodeSkill extends ToolSkill {
|
|
54366
|
+
constructor({ outputRoot = process.cwd(), relativeDirPath = CORTEXCODE_SKILLS_DIR_PATH, dirName, frontmatter, body, otherFiles = [], validate = true, global = false }) {
|
|
51538
54367
|
super({
|
|
51539
54368
|
outputRoot,
|
|
51540
54369
|
relativeDirPath,
|
|
@@ -51552,12 +54381,11 @@ var CopilotcliSkill = class CopilotcliSkill extends ToolSkill {
|
|
|
51552
54381
|
if (!result.success) throw result.error;
|
|
51553
54382
|
}
|
|
51554
54383
|
}
|
|
51555
|
-
static getSettablePaths(
|
|
51556
|
-
|
|
51557
|
-
return { relativeDirPath: COPILOT_SKILLS_DIR_PATH };
|
|
54384
|
+
static getSettablePaths({ global = false } = {}) {
|
|
54385
|
+
return { relativeDirPath: global ? CORTEXCODE_GLOBAL_SKILLS_DIR_PATH : CORTEXCODE_SKILLS_DIR_PATH };
|
|
51558
54386
|
}
|
|
51559
54387
|
getFrontmatter() {
|
|
51560
|
-
return
|
|
54388
|
+
return CortexcodeSkillFrontmatterSchema.parse(this.requireMainFileFrontmatter());
|
|
51561
54389
|
}
|
|
51562
54390
|
getBody() {
|
|
51563
54391
|
return this.mainFile?.body ?? "";
|
|
@@ -51567,23 +54395,27 @@ var CopilotcliSkill = class CopilotcliSkill extends ToolSkill {
|
|
|
51567
54395
|
success: false,
|
|
51568
54396
|
error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME} file does not exist`)
|
|
51569
54397
|
};
|
|
51570
|
-
const result =
|
|
54398
|
+
const result = CortexcodeSkillFrontmatterSchema.safeParse(this.mainFile.frontmatter);
|
|
51571
54399
|
if (!result.success) return {
|
|
51572
54400
|
success: false,
|
|
51573
54401
|
error: /* @__PURE__ */ new Error(`Invalid frontmatter in ${this.getDirPath()}: ${formatError(result.error)}`)
|
|
51574
54402
|
};
|
|
54403
|
+
if (result.data.name !== this.getDirName()) return {
|
|
54404
|
+
success: false,
|
|
54405
|
+
error: /* @__PURE__ */ new Error(`${this.getDirPath()}: frontmatter name (${result.data.name}) must match directory name (${this.getDirName()})`)
|
|
54406
|
+
};
|
|
51575
54407
|
return {
|
|
51576
54408
|
success: true,
|
|
51577
54409
|
error: null
|
|
51578
54410
|
};
|
|
51579
54411
|
}
|
|
51580
54412
|
toRulesyncSkill() {
|
|
51581
|
-
const { name, description, ...
|
|
54413
|
+
const { name, description, ...cortexcodeSection } = this.getFrontmatter();
|
|
51582
54414
|
const rulesyncFrontmatter = {
|
|
51583
54415
|
name,
|
|
51584
54416
|
description,
|
|
51585
54417
|
targets: ["*"],
|
|
51586
|
-
...Object.keys(
|
|
54418
|
+
...Object.keys(cortexcodeSection).length > 0 && { cortexcode: cortexcodeSection }
|
|
51587
54419
|
};
|
|
51588
54420
|
return new RulesyncSkill({
|
|
51589
54421
|
outputRoot: this.outputRoot,
|
|
@@ -51597,35 +54429,19 @@ var CopilotcliSkill = class CopilotcliSkill extends ToolSkill {
|
|
|
51597
54429
|
});
|
|
51598
54430
|
}
|
|
51599
54431
|
static fromRulesyncSkill({ outputRoot = process.cwd(), rulesyncSkill, validate = true, global = false }) {
|
|
51600
|
-
const settablePaths =
|
|
54432
|
+
const settablePaths = CortexcodeSkill.getSettablePaths({ global });
|
|
51601
54433
|
const rulesyncFrontmatter = rulesyncSkill.getFrontmatter();
|
|
51602
|
-
const
|
|
51603
|
-
const
|
|
51604
|
-
|
|
51605
|
-
section: copilotcliSection
|
|
51606
|
-
});
|
|
51607
|
-
const resolvedDisableModelInvocation = resolveDisableModelInvocation({
|
|
51608
|
-
rootFrontmatter: rulesyncFrontmatter,
|
|
51609
|
-
section: copilotcliSection
|
|
51610
|
-
});
|
|
51611
|
-
const license = resolveLicense({
|
|
51612
|
-
rootFrontmatter: rulesyncFrontmatter,
|
|
51613
|
-
section: copilotcliSection
|
|
51614
|
-
});
|
|
51615
|
-
const { "user-invocable": _userInvocable, "disable-model-invocation": _disableModelInvocation, license: _license, ...copilotcliFields } = copilotcliSection ?? {};
|
|
51616
|
-
const copilotcliFrontmatter = {
|
|
51617
|
-
...copilotcliFields,
|
|
54434
|
+
const { name: _sectionName, description: _sectionDescription, ...cortexcodeSection } = rulesyncFrontmatter.cortexcode ?? {};
|
|
54435
|
+
const cortexcodeFrontmatter = {
|
|
54436
|
+
...cortexcodeSection,
|
|
51618
54437
|
name: rulesyncFrontmatter.name,
|
|
51619
|
-
description: rulesyncFrontmatter.description
|
|
51620
|
-
...license !== void 0 && { license },
|
|
51621
|
-
...resolvedUserInvocable !== void 0 && { "user-invocable": resolvedUserInvocable },
|
|
51622
|
-
...resolvedDisableModelInvocation !== void 0 && { "disable-model-invocation": resolvedDisableModelInvocation }
|
|
54438
|
+
description: rulesyncFrontmatter.description
|
|
51623
54439
|
};
|
|
51624
|
-
return new
|
|
54440
|
+
return new CortexcodeSkill({
|
|
51625
54441
|
outputRoot,
|
|
51626
54442
|
relativeDirPath: settablePaths.relativeDirPath,
|
|
51627
|
-
dirName:
|
|
51628
|
-
frontmatter:
|
|
54443
|
+
dirName: cortexcodeFrontmatter.name,
|
|
54444
|
+
frontmatter: cortexcodeFrontmatter,
|
|
51629
54445
|
body: rulesyncSkill.getBody(),
|
|
51630
54446
|
otherFiles: rulesyncSkill.getOtherFiles(),
|
|
51631
54447
|
validate,
|
|
@@ -51634,19 +54450,23 @@ var CopilotcliSkill = class CopilotcliSkill extends ToolSkill {
|
|
|
51634
54450
|
}
|
|
51635
54451
|
static isTargetedByRulesyncSkill(rulesyncSkill) {
|
|
51636
54452
|
const targets = rulesyncSkill.getFrontmatter().targets;
|
|
51637
|
-
return targets.includes("*") || targets.includes("
|
|
54453
|
+
return targets.includes("*") || targets.includes("cortexcode");
|
|
51638
54454
|
}
|
|
51639
54455
|
static async fromDir(params) {
|
|
51640
54456
|
const loaded = await this.loadSkillDirContent({
|
|
51641
54457
|
...params,
|
|
51642
|
-
getSettablePaths:
|
|
54458
|
+
getSettablePaths: CortexcodeSkill.getSettablePaths
|
|
51643
54459
|
});
|
|
51644
|
-
const result =
|
|
54460
|
+
const result = CortexcodeSkillFrontmatterSchema.safeParse(loaded.frontmatter);
|
|
51645
54461
|
if (!result.success) {
|
|
51646
54462
|
const skillDirPath = join(loaded.outputRoot, loaded.relativeDirPath, loaded.dirName);
|
|
51647
54463
|
throw new Error(`Invalid frontmatter in ${join(skillDirPath, SKILL_FILE_NAME)}: ${formatError(result.error)}`);
|
|
51648
54464
|
}
|
|
51649
|
-
|
|
54465
|
+
if (result.data.name !== loaded.dirName) {
|
|
54466
|
+
const skillFilePath = join(loaded.outputRoot, loaded.relativeDirPath, loaded.dirName, SKILL_FILE_NAME);
|
|
54467
|
+
throw new Error(`Frontmatter name (${result.data.name}) must match directory name (${loaded.dirName}) in ${skillFilePath}`);
|
|
54468
|
+
}
|
|
54469
|
+
return new CortexcodeSkill({
|
|
51650
54470
|
outputRoot: loaded.outputRoot,
|
|
51651
54471
|
relativeDirPath: loaded.relativeDirPath,
|
|
51652
54472
|
dirName: loaded.dirName,
|
|
@@ -51658,10 +54478,9 @@ var CopilotcliSkill = class CopilotcliSkill extends ToolSkill {
|
|
|
51658
54478
|
});
|
|
51659
54479
|
}
|
|
51660
54480
|
static forDeletion({ outputRoot = process.cwd(), relativeDirPath, dirName, global = false }) {
|
|
51661
|
-
|
|
51662
|
-
return new CopilotcliSkill({
|
|
54481
|
+
return new CortexcodeSkill({
|
|
51663
54482
|
outputRoot,
|
|
51664
|
-
relativeDirPath
|
|
54483
|
+
relativeDirPath,
|
|
51665
54484
|
dirName,
|
|
51666
54485
|
frontmatter: {
|
|
51667
54486
|
name: "",
|
|
@@ -54985,6 +57804,154 @@ var RooSkill = class RooSkill extends ToolSkill {
|
|
|
54985
57804
|
}
|
|
54986
57805
|
};
|
|
54987
57806
|
//#endregion
|
|
57807
|
+
//#region src/features/skills/tabnine-skill.ts
|
|
57808
|
+
const TabnineSkillFrontmatterSchema = z.looseObject({
|
|
57809
|
+
name: z.string(),
|
|
57810
|
+
description: z.string()
|
|
57811
|
+
});
|
|
57812
|
+
/**
|
|
57813
|
+
* Represents a Tabnine CLI skill directory.
|
|
57814
|
+
*
|
|
57815
|
+
* Tabnine CLI discovers Anthropic-style `<name>/SKILL.md` directories under
|
|
57816
|
+
* `<project>/.tabnine/agent/skills/` (workspace scope) and
|
|
57817
|
+
* `~/.tabnine/agent/skills/` (user scope); the workspace copy wins on a name
|
|
57818
|
+
* clash. The frontmatter requires `name` (matching the directory name) and
|
|
57819
|
+
* `description`; supporting files next to `SKILL.md` are carried along.
|
|
57820
|
+
*
|
|
57821
|
+
* @see https://docs.tabnine.com/main/getting-started/tabnine-cli/features/agent-skills
|
|
57822
|
+
*/
|
|
57823
|
+
var TabnineSkill = class TabnineSkill extends ToolSkill {
|
|
57824
|
+
constructor({ outputRoot = process.cwd(), relativeDirPath = TABNINE_SKILLS_DIR_PATH, dirName, frontmatter, body, otherFiles = [], validate = true, global = false }) {
|
|
57825
|
+
super({
|
|
57826
|
+
outputRoot,
|
|
57827
|
+
relativeDirPath,
|
|
57828
|
+
dirName,
|
|
57829
|
+
mainFile: {
|
|
57830
|
+
name: SKILL_FILE_NAME,
|
|
57831
|
+
body,
|
|
57832
|
+
frontmatter: { ...frontmatter }
|
|
57833
|
+
},
|
|
57834
|
+
otherFiles,
|
|
57835
|
+
global
|
|
57836
|
+
});
|
|
57837
|
+
if (validate) {
|
|
57838
|
+
const result = this.validate();
|
|
57839
|
+
if (!result.success) throw result.error;
|
|
57840
|
+
}
|
|
57841
|
+
}
|
|
57842
|
+
static getSettablePaths({ global: _global = false } = {}) {
|
|
57843
|
+
return { relativeDirPath: TABNINE_SKILLS_DIR_PATH };
|
|
57844
|
+
}
|
|
57845
|
+
getFrontmatter() {
|
|
57846
|
+
return TabnineSkillFrontmatterSchema.parse(this.requireMainFileFrontmatter());
|
|
57847
|
+
}
|
|
57848
|
+
getBody() {
|
|
57849
|
+
return this.mainFile?.body ?? "";
|
|
57850
|
+
}
|
|
57851
|
+
validate() {
|
|
57852
|
+
if (!this.mainFile) return {
|
|
57853
|
+
success: false,
|
|
57854
|
+
error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME} file does not exist`)
|
|
57855
|
+
};
|
|
57856
|
+
const result = TabnineSkillFrontmatterSchema.safeParse(this.mainFile.frontmatter);
|
|
57857
|
+
if (!result.success) return {
|
|
57858
|
+
success: false,
|
|
57859
|
+
error: /* @__PURE__ */ new Error(`Invalid frontmatter in ${this.getDirPath()}: ${formatError(result.error)}`)
|
|
57860
|
+
};
|
|
57861
|
+
if (result.data.name !== this.getDirName()) return {
|
|
57862
|
+
success: false,
|
|
57863
|
+
error: /* @__PURE__ */ new Error(`${this.getDirPath()}: frontmatter name (${result.data.name}) must match directory name (${this.getDirName()})`)
|
|
57864
|
+
};
|
|
57865
|
+
return {
|
|
57866
|
+
success: true,
|
|
57867
|
+
error: null
|
|
57868
|
+
};
|
|
57869
|
+
}
|
|
57870
|
+
toRulesyncSkill() {
|
|
57871
|
+
const { name, description, ...tabnineSection } = this.getFrontmatter();
|
|
57872
|
+
const rulesyncFrontmatter = {
|
|
57873
|
+
name,
|
|
57874
|
+
description,
|
|
57875
|
+
targets: ["*"],
|
|
57876
|
+
...Object.keys(tabnineSection).length > 0 && { tabnine: tabnineSection }
|
|
57877
|
+
};
|
|
57878
|
+
return new RulesyncSkill({
|
|
57879
|
+
outputRoot: this.outputRoot,
|
|
57880
|
+
relativeDirPath: RULESYNC_SKILLS_RELATIVE_DIR_PATH,
|
|
57881
|
+
dirName: this.getDirName(),
|
|
57882
|
+
frontmatter: rulesyncFrontmatter,
|
|
57883
|
+
body: this.getBody(),
|
|
57884
|
+
otherFiles: this.getOtherFiles(),
|
|
57885
|
+
validate: true,
|
|
57886
|
+
global: this.global
|
|
57887
|
+
});
|
|
57888
|
+
}
|
|
57889
|
+
static fromRulesyncSkill({ outputRoot = process.cwd(), rulesyncSkill, validate = true, global = false }) {
|
|
57890
|
+
const settablePaths = TabnineSkill.getSettablePaths({ global });
|
|
57891
|
+
const rulesyncFrontmatter = rulesyncSkill.getFrontmatter();
|
|
57892
|
+
const { name: _sectionName, description: _sectionDescription, ...tabnineSection } = rulesyncFrontmatter.tabnine ?? {};
|
|
57893
|
+
const tabnineFrontmatter = {
|
|
57894
|
+
...tabnineSection,
|
|
57895
|
+
name: rulesyncFrontmatter.name,
|
|
57896
|
+
description: rulesyncFrontmatter.description
|
|
57897
|
+
};
|
|
57898
|
+
return new TabnineSkill({
|
|
57899
|
+
outputRoot,
|
|
57900
|
+
relativeDirPath: settablePaths.relativeDirPath,
|
|
57901
|
+
dirName: tabnineFrontmatter.name,
|
|
57902
|
+
frontmatter: tabnineFrontmatter,
|
|
57903
|
+
body: rulesyncSkill.getBody(),
|
|
57904
|
+
otherFiles: rulesyncSkill.getOtherFiles(),
|
|
57905
|
+
validate,
|
|
57906
|
+
global
|
|
57907
|
+
});
|
|
57908
|
+
}
|
|
57909
|
+
static isTargetedByRulesyncSkill(rulesyncSkill) {
|
|
57910
|
+
const targets = rulesyncSkill.getFrontmatter().targets;
|
|
57911
|
+
return targets.includes("*") || targets.includes("tabnine");
|
|
57912
|
+
}
|
|
57913
|
+
static async fromDir(params) {
|
|
57914
|
+
const loaded = await this.loadSkillDirContent({
|
|
57915
|
+
...params,
|
|
57916
|
+
getSettablePaths: TabnineSkill.getSettablePaths
|
|
57917
|
+
});
|
|
57918
|
+
const result = TabnineSkillFrontmatterSchema.safeParse(loaded.frontmatter);
|
|
57919
|
+
if (!result.success) {
|
|
57920
|
+
const skillDirPath = join(loaded.outputRoot, loaded.relativeDirPath, loaded.dirName);
|
|
57921
|
+
throw new Error(`Invalid frontmatter in ${join(skillDirPath, SKILL_FILE_NAME)}: ${formatError(result.error)}`);
|
|
57922
|
+
}
|
|
57923
|
+
if (result.data.name !== loaded.dirName) {
|
|
57924
|
+
const skillFilePath = join(loaded.outputRoot, loaded.relativeDirPath, loaded.dirName, SKILL_FILE_NAME);
|
|
57925
|
+
throw new Error(`Frontmatter name (${result.data.name}) must match directory name (${loaded.dirName}) in ${skillFilePath}`);
|
|
57926
|
+
}
|
|
57927
|
+
return new TabnineSkill({
|
|
57928
|
+
outputRoot: loaded.outputRoot,
|
|
57929
|
+
relativeDirPath: loaded.relativeDirPath,
|
|
57930
|
+
dirName: loaded.dirName,
|
|
57931
|
+
frontmatter: result.data,
|
|
57932
|
+
body: loaded.body,
|
|
57933
|
+
otherFiles: loaded.otherFiles,
|
|
57934
|
+
validate: true,
|
|
57935
|
+
global: loaded.global
|
|
57936
|
+
});
|
|
57937
|
+
}
|
|
57938
|
+
static forDeletion({ outputRoot = process.cwd(), relativeDirPath, dirName, global = false }) {
|
|
57939
|
+
return new TabnineSkill({
|
|
57940
|
+
outputRoot,
|
|
57941
|
+
relativeDirPath,
|
|
57942
|
+
dirName,
|
|
57943
|
+
frontmatter: {
|
|
57944
|
+
name: "",
|
|
57945
|
+
description: ""
|
|
57946
|
+
},
|
|
57947
|
+
body: "",
|
|
57948
|
+
otherFiles: [],
|
|
57949
|
+
validate: false,
|
|
57950
|
+
global
|
|
57951
|
+
});
|
|
57952
|
+
}
|
|
57953
|
+
};
|
|
57954
|
+
//#endregion
|
|
54988
57955
|
//#region src/features/skills/takt-skill.ts
|
|
54989
57956
|
/**
|
|
54990
57957
|
* Skill generator for TAKT.
|
|
@@ -55873,6 +58840,14 @@ const toolSkillFactories = /* @__PURE__ */ new Map([
|
|
|
55873
58840
|
supportsGlobal: true
|
|
55874
58841
|
}
|
|
55875
58842
|
}],
|
|
58843
|
+
["bob", {
|
|
58844
|
+
class: BobSkill,
|
|
58845
|
+
meta: {
|
|
58846
|
+
supportsProject: true,
|
|
58847
|
+
supportsSimulated: false,
|
|
58848
|
+
supportsGlobal: true
|
|
58849
|
+
}
|
|
58850
|
+
}],
|
|
55876
58851
|
["claudecode", {
|
|
55877
58852
|
class: ClaudecodeSkill,
|
|
55878
58853
|
meta: {
|
|
@@ -55929,6 +58904,14 @@ const toolSkillFactories = /* @__PURE__ */ new Map([
|
|
|
55929
58904
|
supportsGlobal: true
|
|
55930
58905
|
}
|
|
55931
58906
|
}],
|
|
58907
|
+
["cortexcode", {
|
|
58908
|
+
class: CortexcodeSkill,
|
|
58909
|
+
meta: {
|
|
58910
|
+
supportsProject: true,
|
|
58911
|
+
supportsSimulated: false,
|
|
58912
|
+
supportsGlobal: true
|
|
58913
|
+
}
|
|
58914
|
+
}],
|
|
55932
58915
|
["crush", {
|
|
55933
58916
|
class: CrushSkill,
|
|
55934
58917
|
meta: {
|
|
@@ -56106,6 +59089,14 @@ const toolSkillFactories = /* @__PURE__ */ new Map([
|
|
|
56106
59089
|
supportsGlobal: true
|
|
56107
59090
|
}
|
|
56108
59091
|
}],
|
|
59092
|
+
["tabnine", {
|
|
59093
|
+
class: TabnineSkill,
|
|
59094
|
+
meta: {
|
|
59095
|
+
supportsProject: true,
|
|
59096
|
+
supportsSimulated: false,
|
|
59097
|
+
supportsGlobal: true
|
|
59098
|
+
}
|
|
59099
|
+
}],
|
|
56109
59100
|
["takt", {
|
|
56110
59101
|
class: TaktSkill,
|
|
56111
59102
|
meta: {
|
|
@@ -58300,6 +61291,135 @@ var CopilotcliSubagent = class CopilotcliSubagent extends ToolSubagent {
|
|
|
58300
61291
|
}
|
|
58301
61292
|
};
|
|
58302
61293
|
//#endregion
|
|
61294
|
+
//#region src/features/subagents/cortexcode-subagent.ts
|
|
61295
|
+
const CortexcodeSubagentFrontmatterSchema = z.looseObject({
|
|
61296
|
+
name: z.string(),
|
|
61297
|
+
description: z.optional(z.string()),
|
|
61298
|
+
/** Tool names the subagent may use; omitted = every tool. */
|
|
61299
|
+
tools: z.optional(z.array(z.string())),
|
|
61300
|
+
model: z.optional(z.string())
|
|
61301
|
+
});
|
|
61302
|
+
/**
|
|
61303
|
+
* Snowflake Cortex Code subagent: a Markdown file with YAML frontmatter under
|
|
61304
|
+
* `.cortex/agents/` (project) or `~/.snowflake/cortex/agents/` (user).
|
|
61305
|
+
*/
|
|
61306
|
+
var CortexcodeSubagent = class CortexcodeSubagent extends ToolSubagent {
|
|
61307
|
+
frontmatter;
|
|
61308
|
+
body;
|
|
61309
|
+
constructor({ frontmatter, body, fileContent, ...rest }) {
|
|
61310
|
+
if (rest.validate !== false) {
|
|
61311
|
+
const result = CortexcodeSubagentFrontmatterSchema.safeParse(frontmatter);
|
|
61312
|
+
if (!result.success) throw new Error(`Invalid frontmatter in ${join(rest.relativeDirPath, rest.relativeFilePath)}: ${formatError(result.error)}`);
|
|
61313
|
+
}
|
|
61314
|
+
super({
|
|
61315
|
+
...rest,
|
|
61316
|
+
fileContent: fileContent ?? stringifyFrontmatter(body, frontmatter)
|
|
61317
|
+
});
|
|
61318
|
+
this.frontmatter = frontmatter;
|
|
61319
|
+
this.body = body;
|
|
61320
|
+
}
|
|
61321
|
+
static getSettablePaths({ global = false } = {}) {
|
|
61322
|
+
return { relativeDirPath: global ? CORTEXCODE_GLOBAL_AGENTS_DIR_PATH : CORTEXCODE_AGENTS_DIR_PATH };
|
|
61323
|
+
}
|
|
61324
|
+
getFrontmatter() {
|
|
61325
|
+
return this.frontmatter;
|
|
61326
|
+
}
|
|
61327
|
+
getBody() {
|
|
61328
|
+
return this.body;
|
|
61329
|
+
}
|
|
61330
|
+
toRulesyncSubagent() {
|
|
61331
|
+
const { name, description, ...rest } = this.frontmatter;
|
|
61332
|
+
return new RulesyncSubagent({
|
|
61333
|
+
outputRoot: ".",
|
|
61334
|
+
frontmatter: {
|
|
61335
|
+
targets: ["*"],
|
|
61336
|
+
name,
|
|
61337
|
+
description,
|
|
61338
|
+
cortexcode: { ...rest }
|
|
61339
|
+
},
|
|
61340
|
+
body: this.body,
|
|
61341
|
+
relativeDirPath: RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH,
|
|
61342
|
+
relativeFilePath: this.getRelativeFilePath(),
|
|
61343
|
+
validate: true
|
|
61344
|
+
});
|
|
61345
|
+
}
|
|
61346
|
+
static fromRulesyncSubagent({ outputRoot = process.cwd(), rulesyncSubagent, validate = true, global = false }) {
|
|
61347
|
+
const rulesyncFrontmatter = rulesyncSubagent.getFrontmatter();
|
|
61348
|
+
const cortexcodeSection = rulesyncFrontmatter.cortexcode ?? {};
|
|
61349
|
+
const cortexcodeSubagentFrontmatter = {
|
|
61350
|
+
name: rulesyncFrontmatter.name,
|
|
61351
|
+
description: rulesyncFrontmatter.description,
|
|
61352
|
+
...cortexcodeSection
|
|
61353
|
+
};
|
|
61354
|
+
const body = rulesyncSubagent.getBody();
|
|
61355
|
+
const fileContent = stringifyFrontmatter(body, cortexcodeSubagentFrontmatter, { avoidBlockScalars: true });
|
|
61356
|
+
const paths = this.getSettablePaths({ global });
|
|
61357
|
+
return new CortexcodeSubagent({
|
|
61358
|
+
outputRoot,
|
|
61359
|
+
frontmatter: cortexcodeSubagentFrontmatter,
|
|
61360
|
+
body,
|
|
61361
|
+
relativeDirPath: paths.relativeDirPath,
|
|
61362
|
+
relativeFilePath: rulesyncSubagent.getRelativeFilePath(),
|
|
61363
|
+
fileContent,
|
|
61364
|
+
validate,
|
|
61365
|
+
global
|
|
61366
|
+
});
|
|
61367
|
+
}
|
|
61368
|
+
validate() {
|
|
61369
|
+
if (!this.frontmatter) return {
|
|
61370
|
+
success: true,
|
|
61371
|
+
error: null
|
|
61372
|
+
};
|
|
61373
|
+
const result = CortexcodeSubagentFrontmatterSchema.safeParse(this.frontmatter);
|
|
61374
|
+
if (result.success) return {
|
|
61375
|
+
success: true,
|
|
61376
|
+
error: null
|
|
61377
|
+
};
|
|
61378
|
+
else return {
|
|
61379
|
+
success: false,
|
|
61380
|
+
error: /* @__PURE__ */ new Error(`Invalid frontmatter in ${join(this.relativeDirPath, this.relativeFilePath)}: ${formatError(result.error)}`)
|
|
61381
|
+
};
|
|
61382
|
+
}
|
|
61383
|
+
static isTargetedByRulesyncSubagent(rulesyncSubagent) {
|
|
61384
|
+
return this.isTargetedByRulesyncSubagentDefault({
|
|
61385
|
+
rulesyncSubagent,
|
|
61386
|
+
toolTarget: "cortexcode"
|
|
61387
|
+
});
|
|
61388
|
+
}
|
|
61389
|
+
static async fromFile({ outputRoot = process.cwd(), relativeFilePath, validate = true, global = false }) {
|
|
61390
|
+
const paths = this.getSettablePaths({ global });
|
|
61391
|
+
const filePath = join(outputRoot, paths.relativeDirPath, relativeFilePath);
|
|
61392
|
+
const fileContent = await readFileContent(filePath);
|
|
61393
|
+
const { frontmatter, body: content } = parseFrontmatter(fileContent, filePath);
|
|
61394
|
+
const result = CortexcodeSubagentFrontmatterSchema.safeParse(frontmatter);
|
|
61395
|
+
if (!result.success) throw new Error(`Invalid frontmatter in ${filePath}: ${formatError(result.error)}`);
|
|
61396
|
+
return new CortexcodeSubagent({
|
|
61397
|
+
outputRoot,
|
|
61398
|
+
relativeDirPath: paths.relativeDirPath,
|
|
61399
|
+
relativeFilePath,
|
|
61400
|
+
frontmatter: result.data,
|
|
61401
|
+
body: content.trim(),
|
|
61402
|
+
fileContent,
|
|
61403
|
+
validate,
|
|
61404
|
+
global
|
|
61405
|
+
});
|
|
61406
|
+
}
|
|
61407
|
+
static forDeletion({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath }) {
|
|
61408
|
+
return new CortexcodeSubagent({
|
|
61409
|
+
outputRoot,
|
|
61410
|
+
relativeDirPath,
|
|
61411
|
+
relativeFilePath,
|
|
61412
|
+
frontmatter: {
|
|
61413
|
+
name: "",
|
|
61414
|
+
description: ""
|
|
61415
|
+
},
|
|
61416
|
+
body: "",
|
|
61417
|
+
fileContent: "",
|
|
61418
|
+
validate: false
|
|
61419
|
+
});
|
|
61420
|
+
}
|
|
61421
|
+
};
|
|
61422
|
+
//#endregion
|
|
58303
61423
|
//#region src/features/subagents/cursor-subagent.ts
|
|
58304
61424
|
const CursorSubagentFrontmatterSchema = z.looseObject({
|
|
58305
61425
|
name: z.string(),
|
|
@@ -60752,6 +63872,145 @@ var RooSubagent = class extends ToolSubagent {
|
|
|
60752
63872
|
}
|
|
60753
63873
|
};
|
|
60754
63874
|
//#endregion
|
|
63875
|
+
//#region src/features/subagents/tabnine-subagent.ts
|
|
63876
|
+
const TabnineSubagentFrontmatterSchema = z.looseObject({
|
|
63877
|
+
name: z.string(),
|
|
63878
|
+
description: z.optional(z.string()),
|
|
63879
|
+
/** "local" (default) runs inside the CLI; "remote" delegates to a Tabnine cloud agent. */
|
|
63880
|
+
kind: z.optional(z.string()),
|
|
63881
|
+
/** Built-in tool names the subagent may use; omitted = every tool. */
|
|
63882
|
+
tools: z.optional(z.array(z.string())),
|
|
63883
|
+
model: z.optional(z.string()),
|
|
63884
|
+
/** Sampling temperature, 0.0-2.0. */
|
|
63885
|
+
temperature: z.optional(z.number()),
|
|
63886
|
+
/** Maximum agent turns (Tabnine default: 15). */
|
|
63887
|
+
max_turns: z.optional(z.number()),
|
|
63888
|
+
/** Wall-clock budget in minutes (Tabnine default: 5). */
|
|
63889
|
+
timeout_mins: z.optional(z.number())
|
|
63890
|
+
});
|
|
63891
|
+
/**
|
|
63892
|
+
* Tabnine CLI subagent: a Markdown file with YAML frontmatter under
|
|
63893
|
+
* `.tabnine/agent/agents/` (project) or `~/.tabnine/agent/agents/` (user).
|
|
63894
|
+
* Tabnine only loads these when `experimental.enableAgents` is `true` in its
|
|
63895
|
+
* settings; rulesync writes the files and leaves that flag to the user.
|
|
63896
|
+
*/
|
|
63897
|
+
var TabnineSubagent = class TabnineSubagent extends ToolSubagent {
|
|
63898
|
+
frontmatter;
|
|
63899
|
+
body;
|
|
63900
|
+
constructor({ frontmatter, body, fileContent, ...rest }) {
|
|
63901
|
+
if (rest.validate !== false) {
|
|
63902
|
+
const result = TabnineSubagentFrontmatterSchema.safeParse(frontmatter);
|
|
63903
|
+
if (!result.success) throw new Error(`Invalid frontmatter in ${join(rest.relativeDirPath, rest.relativeFilePath)}: ${formatError(result.error)}`);
|
|
63904
|
+
}
|
|
63905
|
+
super({
|
|
63906
|
+
...rest,
|
|
63907
|
+
fileContent: fileContent ?? stringifyFrontmatter(body, frontmatter)
|
|
63908
|
+
});
|
|
63909
|
+
this.frontmatter = frontmatter;
|
|
63910
|
+
this.body = body;
|
|
63911
|
+
}
|
|
63912
|
+
static getSettablePaths(_options = {}) {
|
|
63913
|
+
return { relativeDirPath: TABNINE_AGENTS_DIR_PATH };
|
|
63914
|
+
}
|
|
63915
|
+
getFrontmatter() {
|
|
63916
|
+
return this.frontmatter;
|
|
63917
|
+
}
|
|
63918
|
+
getBody() {
|
|
63919
|
+
return this.body;
|
|
63920
|
+
}
|
|
63921
|
+
toRulesyncSubagent() {
|
|
63922
|
+
const { name, description, ...rest } = this.frontmatter;
|
|
63923
|
+
return new RulesyncSubagent({
|
|
63924
|
+
outputRoot: ".",
|
|
63925
|
+
frontmatter: {
|
|
63926
|
+
targets: ["*"],
|
|
63927
|
+
name,
|
|
63928
|
+
description,
|
|
63929
|
+
tabnine: { ...rest }
|
|
63930
|
+
},
|
|
63931
|
+
body: this.body,
|
|
63932
|
+
relativeDirPath: RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH,
|
|
63933
|
+
relativeFilePath: this.getRelativeFilePath(),
|
|
63934
|
+
validate: true
|
|
63935
|
+
});
|
|
63936
|
+
}
|
|
63937
|
+
static fromRulesyncSubagent({ outputRoot = process.cwd(), rulesyncSubagent, validate = true, global = false }) {
|
|
63938
|
+
const rulesyncFrontmatter = rulesyncSubagent.getFrontmatter();
|
|
63939
|
+
const tabnineSection = rulesyncFrontmatter.tabnine ?? {};
|
|
63940
|
+
const tabnineSubagentFrontmatter = {
|
|
63941
|
+
name: rulesyncFrontmatter.name,
|
|
63942
|
+
description: rulesyncFrontmatter.description,
|
|
63943
|
+
...tabnineSection
|
|
63944
|
+
};
|
|
63945
|
+
const body = rulesyncSubagent.getBody();
|
|
63946
|
+
const fileContent = stringifyFrontmatter(body, tabnineSubagentFrontmatter, { avoidBlockScalars: true });
|
|
63947
|
+
const paths = this.getSettablePaths({ global });
|
|
63948
|
+
return new TabnineSubagent({
|
|
63949
|
+
outputRoot,
|
|
63950
|
+
frontmatter: tabnineSubagentFrontmatter,
|
|
63951
|
+
body,
|
|
63952
|
+
relativeDirPath: paths.relativeDirPath,
|
|
63953
|
+
relativeFilePath: rulesyncSubagent.getRelativeFilePath(),
|
|
63954
|
+
fileContent,
|
|
63955
|
+
validate,
|
|
63956
|
+
global
|
|
63957
|
+
});
|
|
63958
|
+
}
|
|
63959
|
+
validate() {
|
|
63960
|
+
if (!this.frontmatter) return {
|
|
63961
|
+
success: true,
|
|
63962
|
+
error: null
|
|
63963
|
+
};
|
|
63964
|
+
const result = TabnineSubagentFrontmatterSchema.safeParse(this.frontmatter);
|
|
63965
|
+
if (result.success) return {
|
|
63966
|
+
success: true,
|
|
63967
|
+
error: null
|
|
63968
|
+
};
|
|
63969
|
+
else return {
|
|
63970
|
+
success: false,
|
|
63971
|
+
error: /* @__PURE__ */ new Error(`Invalid frontmatter in ${join(this.relativeDirPath, this.relativeFilePath)}: ${formatError(result.error)}`)
|
|
63972
|
+
};
|
|
63973
|
+
}
|
|
63974
|
+
static isTargetedByRulesyncSubagent(rulesyncSubagent) {
|
|
63975
|
+
return this.isTargetedByRulesyncSubagentDefault({
|
|
63976
|
+
rulesyncSubagent,
|
|
63977
|
+
toolTarget: "tabnine"
|
|
63978
|
+
});
|
|
63979
|
+
}
|
|
63980
|
+
static async fromFile({ outputRoot = process.cwd(), relativeFilePath, validate = true, global = false }) {
|
|
63981
|
+
const paths = this.getSettablePaths({ global });
|
|
63982
|
+
const filePath = join(outputRoot, paths.relativeDirPath, relativeFilePath);
|
|
63983
|
+
const fileContent = await readFileContent(filePath);
|
|
63984
|
+
const { frontmatter, body: content } = parseFrontmatter(fileContent, filePath);
|
|
63985
|
+
const result = TabnineSubagentFrontmatterSchema.safeParse(frontmatter);
|
|
63986
|
+
if (!result.success) throw new Error(`Invalid frontmatter in ${filePath}: ${formatError(result.error)}`);
|
|
63987
|
+
return new TabnineSubagent({
|
|
63988
|
+
outputRoot,
|
|
63989
|
+
relativeDirPath: paths.relativeDirPath,
|
|
63990
|
+
relativeFilePath,
|
|
63991
|
+
frontmatter: result.data,
|
|
63992
|
+
body: content.trim(),
|
|
63993
|
+
fileContent,
|
|
63994
|
+
validate,
|
|
63995
|
+
global
|
|
63996
|
+
});
|
|
63997
|
+
}
|
|
63998
|
+
static forDeletion({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath }) {
|
|
63999
|
+
return new TabnineSubagent({
|
|
64000
|
+
outputRoot,
|
|
64001
|
+
relativeDirPath,
|
|
64002
|
+
relativeFilePath,
|
|
64003
|
+
frontmatter: {
|
|
64004
|
+
name: "",
|
|
64005
|
+
description: ""
|
|
64006
|
+
},
|
|
64007
|
+
body: "",
|
|
64008
|
+
fileContent: "",
|
|
64009
|
+
validate: false
|
|
64010
|
+
});
|
|
64011
|
+
}
|
|
64012
|
+
};
|
|
64013
|
+
//#endregion
|
|
60755
64014
|
//#region src/features/subagents/takt-subagent.ts
|
|
60756
64015
|
/**
|
|
60757
64016
|
* Subagent generator for TAKT.
|
|
@@ -61383,6 +64642,15 @@ const toolSubagentFactories = /* @__PURE__ */ new Map([
|
|
|
61383
64642
|
filePattern: "*.agent.md"
|
|
61384
64643
|
}
|
|
61385
64644
|
}],
|
|
64645
|
+
["cortexcode", {
|
|
64646
|
+
class: CortexcodeSubagent,
|
|
64647
|
+
meta: {
|
|
64648
|
+
supportsProject: true,
|
|
64649
|
+
supportsSimulated: false,
|
|
64650
|
+
supportsGlobal: true,
|
|
64651
|
+
filePattern: "*.md"
|
|
64652
|
+
}
|
|
64653
|
+
}],
|
|
61386
64654
|
["cursor", {
|
|
61387
64655
|
class: CursorSubagent,
|
|
61388
64656
|
meta: {
|
|
@@ -61554,6 +64822,15 @@ const toolSubagentFactories = /* @__PURE__ */ new Map([
|
|
|
61554
64822
|
filePattern: "*.md"
|
|
61555
64823
|
}
|
|
61556
64824
|
}],
|
|
64825
|
+
["tabnine", {
|
|
64826
|
+
class: TabnineSubagent,
|
|
64827
|
+
meta: {
|
|
64828
|
+
supportsProject: true,
|
|
64829
|
+
supportsSimulated: false,
|
|
64830
|
+
supportsGlobal: true,
|
|
64831
|
+
filePattern: "*.md"
|
|
64832
|
+
}
|
|
64833
|
+
}],
|
|
61557
64834
|
["takt", {
|
|
61558
64835
|
class: TaktSubagent,
|
|
61559
64836
|
meta: {
|
|
@@ -63105,6 +66382,95 @@ var AugmentcodeRule = class AugmentcodeRule extends ToolRule {
|
|
|
63105
66382
|
}
|
|
63106
66383
|
};
|
|
63107
66384
|
//#endregion
|
|
66385
|
+
//#region src/features/rules/bob-rule.ts
|
|
66386
|
+
/**
|
|
66387
|
+
* Rule generator for IBM Bob (Bob IDE and Bob Shell).
|
|
66388
|
+
*
|
|
66389
|
+
* Bob auto-loads plain-markdown instruction files with no frontmatter:
|
|
66390
|
+
*
|
|
66391
|
+
* - Project scope: the cross-tool `AGENTS.md` at the project root plus every
|
|
66392
|
+
* `.bob/rules/*.md` file (read recursively in alphabetical order).
|
|
66393
|
+
* - Global scope: `~/.bob/AGENTS.md` plus `~/.bob/rules/*.md`.
|
|
66394
|
+
*
|
|
66395
|
+
* The workspace files override the user files. Bob's mode-specific
|
|
66396
|
+
* `.bob/rules-{mode}/` directories are left alone: rulesync has no notion of
|
|
66397
|
+
* Bob's modes, and a rule that should apply everywhere belongs in `.bob/rules/`.
|
|
66398
|
+
*
|
|
66399
|
+
* @see https://bob.ibm.com/docs/ide/configuration/rules
|
|
66400
|
+
* @see https://bob.ibm.com/docs/shell/configuration/configuring (user-scoped `~/.bob/AGENTS.md`)
|
|
66401
|
+
*/
|
|
66402
|
+
var BobRule = class BobRule extends ToolRule {
|
|
66403
|
+
static getSettablePaths({ global, excludeToolDir } = {}) {
|
|
66404
|
+
return {
|
|
66405
|
+
root: {
|
|
66406
|
+
relativeDirPath: global ? buildToolPath(BOB_DIR, ".", excludeToolDir) : ".",
|
|
66407
|
+
relativeFilePath: BOB_RULE_FILE_NAME
|
|
66408
|
+
},
|
|
66409
|
+
nonRoot: { relativeDirPath: buildToolPath(BOB_DIR, "rules", excludeToolDir) }
|
|
66410
|
+
};
|
|
66411
|
+
}
|
|
66412
|
+
static async fromFile({ outputRoot = process.cwd(), relativeFilePath, validate = true, global = false }) {
|
|
66413
|
+
const paths = this.getSettablePaths({ global });
|
|
66414
|
+
if (relativeFilePath === paths.root.relativeFilePath) {
|
|
66415
|
+
const fileContent = await readFileContent(join(outputRoot, paths.root.relativeDirPath, paths.root.relativeFilePath));
|
|
66416
|
+
return new BobRule({
|
|
66417
|
+
outputRoot,
|
|
66418
|
+
relativeDirPath: paths.root.relativeDirPath,
|
|
66419
|
+
relativeFilePath: paths.root.relativeFilePath,
|
|
66420
|
+
fileContent,
|
|
66421
|
+
validate,
|
|
66422
|
+
root: true
|
|
66423
|
+
});
|
|
66424
|
+
}
|
|
66425
|
+
const fileContent = await readFileContent(join(outputRoot, paths.nonRoot.relativeDirPath, relativeFilePath));
|
|
66426
|
+
return new BobRule({
|
|
66427
|
+
outputRoot,
|
|
66428
|
+
relativeDirPath: paths.nonRoot.relativeDirPath,
|
|
66429
|
+
relativeFilePath,
|
|
66430
|
+
fileContent,
|
|
66431
|
+
validate,
|
|
66432
|
+
root: false
|
|
66433
|
+
});
|
|
66434
|
+
}
|
|
66435
|
+
static fromRulesyncRule({ outputRoot = process.cwd(), rulesyncRule, validate = true, global = false }) {
|
|
66436
|
+
const paths = this.getSettablePaths({ global });
|
|
66437
|
+
return new BobRule(this.buildToolRuleParamsDefault({
|
|
66438
|
+
outputRoot,
|
|
66439
|
+
rulesyncRule,
|
|
66440
|
+
validate,
|
|
66441
|
+
rootPath: paths.root,
|
|
66442
|
+
nonRootPath: paths.nonRoot
|
|
66443
|
+
}));
|
|
66444
|
+
}
|
|
66445
|
+
toRulesyncRule() {
|
|
66446
|
+
return this.toRulesyncRuleDefault();
|
|
66447
|
+
}
|
|
66448
|
+
validate() {
|
|
66449
|
+
return {
|
|
66450
|
+
success: true,
|
|
66451
|
+
error: null
|
|
66452
|
+
};
|
|
66453
|
+
}
|
|
66454
|
+
static forDeletion({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath, global = false }) {
|
|
66455
|
+
const paths = this.getSettablePaths({ global });
|
|
66456
|
+
const isRoot = relativeFilePath === paths.root.relativeFilePath && relativeDirPath !== paths.nonRoot.relativeDirPath;
|
|
66457
|
+
return new BobRule({
|
|
66458
|
+
outputRoot,
|
|
66459
|
+
relativeDirPath,
|
|
66460
|
+
relativeFilePath,
|
|
66461
|
+
fileContent: "",
|
|
66462
|
+
validate: false,
|
|
66463
|
+
root: isRoot
|
|
66464
|
+
});
|
|
66465
|
+
}
|
|
66466
|
+
static isTargetedByRulesyncRule(rulesyncRule) {
|
|
66467
|
+
return this.isTargetedByRulesyncRuleDefault({
|
|
66468
|
+
rulesyncRule,
|
|
66469
|
+
toolTarget: "bob"
|
|
66470
|
+
});
|
|
66471
|
+
}
|
|
66472
|
+
};
|
|
66473
|
+
//#endregion
|
|
63108
66474
|
//#region src/features/rules/claudecode-language-settings.ts
|
|
63109
66475
|
/**
|
|
63110
66476
|
* Claude Code's native home for the root `language` key of `rulesync.jsonc`.
|
|
@@ -64319,6 +67685,73 @@ var CopilotcliRule = class CopilotcliRule extends CopilotRule {
|
|
|
64319
67685
|
}
|
|
64320
67686
|
};
|
|
64321
67687
|
//#endregion
|
|
67688
|
+
//#region src/features/rules/cortexcode-rule.ts
|
|
67689
|
+
var CortexcodeRule = class CortexcodeRule extends ToolRule {
|
|
67690
|
+
constructor({ fileContent, root, ...rest }) {
|
|
67691
|
+
super({
|
|
67692
|
+
...rest,
|
|
67693
|
+
fileContent,
|
|
67694
|
+
root: root ?? false
|
|
67695
|
+
});
|
|
67696
|
+
}
|
|
67697
|
+
static getSettablePaths(_options = {}) {
|
|
67698
|
+
return { root: {
|
|
67699
|
+
relativeDirPath: ".",
|
|
67700
|
+
relativeFilePath: CORTEXCODE_RULE_FILE_NAME
|
|
67701
|
+
} };
|
|
67702
|
+
}
|
|
67703
|
+
static async fromFile({ outputRoot = process.cwd(), relativeFilePath: _relativeFilePath, validate = true, global = false }) {
|
|
67704
|
+
const { root } = this.getSettablePaths({ global });
|
|
67705
|
+
const relativePath = join(root.relativeDirPath, root.relativeFilePath);
|
|
67706
|
+
const fileContent = await readFileContent(join(outputRoot, relativePath));
|
|
67707
|
+
return new CortexcodeRule({
|
|
67708
|
+
outputRoot,
|
|
67709
|
+
relativeDirPath: root.relativeDirPath,
|
|
67710
|
+
relativeFilePath: root.relativeFilePath,
|
|
67711
|
+
fileContent,
|
|
67712
|
+
validate,
|
|
67713
|
+
root: true
|
|
67714
|
+
});
|
|
67715
|
+
}
|
|
67716
|
+
static fromRulesyncRule({ outputRoot = process.cwd(), rulesyncRule, validate = true, global = false }) {
|
|
67717
|
+
const { root } = this.getSettablePaths({ global });
|
|
67718
|
+
const isRoot = rulesyncRule.getFrontmatter().root ?? false;
|
|
67719
|
+
return new CortexcodeRule({
|
|
67720
|
+
outputRoot,
|
|
67721
|
+
relativeDirPath: root.relativeDirPath,
|
|
67722
|
+
relativeFilePath: root.relativeFilePath,
|
|
67723
|
+
fileContent: rulesyncRule.getBody(),
|
|
67724
|
+
validate,
|
|
67725
|
+
root: isRoot
|
|
67726
|
+
});
|
|
67727
|
+
}
|
|
67728
|
+
toRulesyncRule() {
|
|
67729
|
+
return this.toRulesyncRuleDefault();
|
|
67730
|
+
}
|
|
67731
|
+
validate() {
|
|
67732
|
+
return {
|
|
67733
|
+
success: true,
|
|
67734
|
+
error: null
|
|
67735
|
+
};
|
|
67736
|
+
}
|
|
67737
|
+
static forDeletion({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath }) {
|
|
67738
|
+
return new CortexcodeRule({
|
|
67739
|
+
outputRoot,
|
|
67740
|
+
relativeDirPath,
|
|
67741
|
+
relativeFilePath,
|
|
67742
|
+
fileContent: "",
|
|
67743
|
+
validate: false,
|
|
67744
|
+
root: relativeFilePath === "AGENTS.md" && relativeDirPath === "."
|
|
67745
|
+
});
|
|
67746
|
+
}
|
|
67747
|
+
static isTargetedByRulesyncRule(rulesyncRule) {
|
|
67748
|
+
return this.isTargetedByRulesyncRuleDefault({
|
|
67749
|
+
rulesyncRule,
|
|
67750
|
+
toolTarget: "cortexcode"
|
|
67751
|
+
});
|
|
67752
|
+
}
|
|
67753
|
+
};
|
|
67754
|
+
//#endregion
|
|
64322
67755
|
//#region src/features/rules/crush-rule.ts
|
|
64323
67756
|
var CrushRule = class CrushRule extends ToolRule {
|
|
64324
67757
|
constructor({ fileContent, root, ...rest }) {
|
|
@@ -67396,6 +70829,97 @@ var RovodevRule = class RovodevRule extends ToolRule {
|
|
|
67396
70829
|
}
|
|
67397
70830
|
};
|
|
67398
70831
|
//#endregion
|
|
70832
|
+
//#region src/features/rules/tabnine-rule.ts
|
|
70833
|
+
/**
|
|
70834
|
+
* Rule generator for Tabnine CLI.
|
|
70835
|
+
*
|
|
70836
|
+
* - Project scope: `TABNINE.md` at the project root is the CLI's memory file
|
|
70837
|
+
* (loaded on every session, refreshed with `/memory reload`). Non-root
|
|
70838
|
+
* rules go to `.tabnine/guidelines/*.md`, the guideline directory the
|
|
70839
|
+
* Tabnine IDE agent reads; the CLI does not auto-load that directory, so
|
|
70840
|
+
* the processor lists the files from `TABNINE.md` (toon mode).
|
|
70841
|
+
* - Global scope: `~/.tabnine/agent/TABNINE.md` next to the other user-scoped
|
|
70842
|
+
* agent assets (the CLI documents no user-level memory file, so this path
|
|
70843
|
+
* mirrors the `~/.gemini/GEMINI.md` convention of the Gemini CLI it is
|
|
70844
|
+
* derived from) plus `~/.tabnine/guidelines/*.md`.
|
|
70845
|
+
*
|
|
70846
|
+
* Rule files are plain Markdown without frontmatter.
|
|
70847
|
+
*
|
|
70848
|
+
* @see https://docs.tabnine.com/main/getting-started/tabnine-cli/features/settings/settings-reference (`context.fileName`, `/memory`)
|
|
70849
|
+
* @see https://docs.tabnine.com/main/getting-started/tabnine-agent/guidelines (`.tabnine/guidelines/`)
|
|
70850
|
+
*/
|
|
70851
|
+
var TabnineRule = class TabnineRule extends ToolRule {
|
|
70852
|
+
static getSettablePaths({ global, excludeToolDir } = {}) {
|
|
70853
|
+
return {
|
|
70854
|
+
root: {
|
|
70855
|
+
relativeDirPath: global ? buildToolPath(TABNINE_AGENT_DIR_PATH, ".", excludeToolDir) : ".",
|
|
70856
|
+
relativeFilePath: TABNINE_RULE_FILE_NAME
|
|
70857
|
+
},
|
|
70858
|
+
nonRoot: { relativeDirPath: buildToolPath(TABNINE_DIR, "guidelines", excludeToolDir) }
|
|
70859
|
+
};
|
|
70860
|
+
}
|
|
70861
|
+
static async fromFile({ outputRoot = process.cwd(), relativeFilePath, validate = true, global = false }) {
|
|
70862
|
+
const paths = this.getSettablePaths({ global });
|
|
70863
|
+
if (relativeFilePath === paths.root.relativeFilePath) {
|
|
70864
|
+
const fileContent = await readFileContent(join(outputRoot, paths.root.relativeDirPath, paths.root.relativeFilePath));
|
|
70865
|
+
return new TabnineRule({
|
|
70866
|
+
outputRoot,
|
|
70867
|
+
relativeDirPath: paths.root.relativeDirPath,
|
|
70868
|
+
relativeFilePath: paths.root.relativeFilePath,
|
|
70869
|
+
fileContent,
|
|
70870
|
+
validate,
|
|
70871
|
+
root: true
|
|
70872
|
+
});
|
|
70873
|
+
}
|
|
70874
|
+
const fileContent = await readFileContent(join(outputRoot, paths.nonRoot.relativeDirPath, relativeFilePath));
|
|
70875
|
+
return new TabnineRule({
|
|
70876
|
+
outputRoot,
|
|
70877
|
+
relativeDirPath: paths.nonRoot.relativeDirPath,
|
|
70878
|
+
relativeFilePath,
|
|
70879
|
+
fileContent,
|
|
70880
|
+
validate,
|
|
70881
|
+
root: false
|
|
70882
|
+
});
|
|
70883
|
+
}
|
|
70884
|
+
static fromRulesyncRule({ outputRoot = process.cwd(), rulesyncRule, validate = true, global = false }) {
|
|
70885
|
+
const paths = this.getSettablePaths({ global });
|
|
70886
|
+
return new TabnineRule(this.buildToolRuleParamsDefault({
|
|
70887
|
+
outputRoot,
|
|
70888
|
+
rulesyncRule,
|
|
70889
|
+
validate,
|
|
70890
|
+
rootPath: paths.root,
|
|
70891
|
+
nonRootPath: paths.nonRoot
|
|
70892
|
+
}));
|
|
70893
|
+
}
|
|
70894
|
+
toRulesyncRule() {
|
|
70895
|
+
return this.toRulesyncRuleDefault();
|
|
70896
|
+
}
|
|
70897
|
+
validate() {
|
|
70898
|
+
return {
|
|
70899
|
+
success: true,
|
|
70900
|
+
error: null
|
|
70901
|
+
};
|
|
70902
|
+
}
|
|
70903
|
+
static forDeletion({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath, global = false }) {
|
|
70904
|
+
const paths = this.getSettablePaths({ global });
|
|
70905
|
+
const isRoot = relativeFilePath === paths.root.relativeFilePath && relativeDirPath !== paths.nonRoot.relativeDirPath;
|
|
70906
|
+
return new TabnineRule({
|
|
70907
|
+
outputRoot,
|
|
70908
|
+
relativeDirPath,
|
|
70909
|
+
relativeFilePath,
|
|
70910
|
+
fileContent: "",
|
|
70911
|
+
validate: false,
|
|
70912
|
+
root: isRoot
|
|
70913
|
+
});
|
|
70914
|
+
}
|
|
70915
|
+
static isTargetedByRulesyncRule(rulesyncRule) {
|
|
70916
|
+
return this.isTargetedByRulesyncRuleDefault({
|
|
70917
|
+
rulesyncRule,
|
|
70918
|
+
toolTarget: "tabnine"
|
|
70919
|
+
});
|
|
70920
|
+
}
|
|
70921
|
+
};
|
|
70922
|
+
//#endregion
|
|
67399
70923
|
//#region src/features/rules/takt-rule.ts
|
|
67400
70924
|
/**
|
|
67401
70925
|
* Default facet directory for TAKT rule files.
|
|
@@ -67958,6 +71482,14 @@ const toolRuleFactories = /* @__PURE__ */ new Map([
|
|
|
67958
71482
|
ruleDiscoveryMode: "toon"
|
|
67959
71483
|
}
|
|
67960
71484
|
}],
|
|
71485
|
+
["bob", {
|
|
71486
|
+
class: BobRule,
|
|
71487
|
+
meta: {
|
|
71488
|
+
extension: "md",
|
|
71489
|
+
supportsGlobal: true,
|
|
71490
|
+
ruleDiscoveryMode: "auto"
|
|
71491
|
+
}
|
|
71492
|
+
}],
|
|
67961
71493
|
["claudecode", {
|
|
67962
71494
|
class: ClaudecodeRule,
|
|
67963
71495
|
meta: {
|
|
@@ -68021,6 +71553,15 @@ const toolRuleFactories = /* @__PURE__ */ new Map([
|
|
|
68021
71553
|
ruleDiscoveryMode: "auto"
|
|
68022
71554
|
}
|
|
68023
71555
|
}],
|
|
71556
|
+
["cortexcode", {
|
|
71557
|
+
class: CortexcodeRule,
|
|
71558
|
+
meta: {
|
|
71559
|
+
extension: "md",
|
|
71560
|
+
supportsGlobal: false,
|
|
71561
|
+
ruleDiscoveryMode: "auto",
|
|
71562
|
+
collisionPolicy: "fold"
|
|
71563
|
+
}
|
|
71564
|
+
}],
|
|
68024
71565
|
["crush", {
|
|
68025
71566
|
class: CrushRule,
|
|
68026
71567
|
meta: {
|
|
@@ -68227,6 +71768,14 @@ const toolRuleFactories = /* @__PURE__ */ new Map([
|
|
|
68227
71768
|
localRootFileName: "AGENTS.local.md"
|
|
68228
71769
|
}
|
|
68229
71770
|
}],
|
|
71771
|
+
["tabnine", {
|
|
71772
|
+
class: TabnineRule,
|
|
71773
|
+
meta: {
|
|
71774
|
+
extension: "md",
|
|
71775
|
+
supportsGlobal: true,
|
|
71776
|
+
ruleDiscoveryMode: "toon"
|
|
71777
|
+
}
|
|
71778
|
+
}],
|
|
68230
71779
|
["takt", {
|
|
68231
71780
|
class: TaktRule,
|
|
68232
71781
|
meta: {
|
|
@@ -71474,6 +75023,6 @@ async function importChecksCore(params) {
|
|
|
71474
75023
|
return writtenCount;
|
|
71475
75024
|
}
|
|
71476
75025
|
//#endregion
|
|
71477
|
-
export { RulesyncCheck as $,
|
|
75026
|
+
export { RulesyncCheck as $, writeFileContent as $t, CLAUDECODE_SKILLS_DIR_PATH as A, RULESYNC_SKILLS_RELATIVE_DIR_PATH as An, directoryExists as At, RulesyncSkill as B, truncateText as Bn, listSubdirectoryNames as Bt, ChecksProcessor as C, RULESYNC_NPM_SOURCES_LOCK_RELATIVE_FILE_PATH as Cn, ErrorCodes as Ct, CLAUDECODE_LOCAL_RULE_FILE_NAME as D, RULESYNC_PERMISSIONS_SCHEMA_URL as Dn, assertWritablePathInsideRoot as Dt, CLAUDECODE_DIR as E, RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH as En, assertTreeContainsNoSymlinks as Et, AUGMENTCODE_DIR as F, parseCommaSeparatedList as Fn, isFileNotFoundError as Ft, RulesyncMcp as G, stripControlCharactersKeepingLineFeeds as Gn, removeDirectoryStrict as Gt, RulesyncRule as H, hasEnclosingMarkOutsideKeycap as Hn, readFileContent as Ht, AUGMENTCODE_SETTINGS_LOCAL_FILE_NAME as I, ALL_FEATURES as In, isFileSystemError as It, getRulesyncSourceCandidates as J, removeTempDirectory as Jt, RulesyncIgnore as K, stripHiddenCharacters as Kn, removeFile as Kt, getLocalSkillDirNames as L, ALL_FEATURES_WITH_WILDCARD as Ln, isSymlink as Lt, FACTORYDROID_SETTINGS_LOCAL_FILE_NAME as M, RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH as Mn, fileExists as Mt, caseFoldIdentity as N, RULESYNC_USER_CONFIG_DIR_NAME as Nn, getFileSize as Nt, CLAUDECODE_MEMORIES_DIR_NAME as O, RULESYNC_RELATIVE_DIR_PATH as On, checkPathTraversal as Ot, groupSpellingsByCaseFoldedIdentity as P, RULESYNC_XDG_CONFIG_HOME_DEFAULT_DIR_NAME as Pn, getHomeDirectory as Pt, RulesyncCommandFrontmatterSchema as Q, writeFileBuffer as Qt, RulesyncSubagent as R, DEPRECATED_FEATURE_REPLACEMENTS as Rn, listDirectoryEntryNames as Rt, QWENCODE_LOCAL_RULE_FILE_NAME as S, RULESYNC_MCP_SCHEMA_URL as Sn, CLIError as St, CODEXCLI_DIR as T, RULESYNC_PERMISSIONS_LEGACY_FILE_NAME as Tn, assertDirectoryIfExists as Tt, RulesyncRuleFrontmatterSchema as U, quoteForLog as Un, readFileContentOrNull as Ut, RulesyncSkillFrontmatterSchema as V, hasDeceptiveHiddenCharacters as Vn, pathEscapesRoot as Vt, RulesyncPermissions as W, stripControlCharacters as Wn, removeDirectory as Wt, parseJsonc as X, runWithDirectoryRollback as Xt, resolveRulesyncSourceWritePath as Y, resolvePath as Yt, RulesyncCommand as Z, toPosixPath as Zt, IgnoreProcessor as _, RULESYNC_IGNORE_RELATIVE_FILE_PATH as _n, fallbackLogger as _t, getProcessorRegistryEntry as a, MAX_FILE_SIZE as an, ConfigResolver as at, CommandsProcessor as b, RULESYNC_MCP_LEGACY_FILE_NAME as bn, resetRunWarningState as bt, RulesProcessor as c, RULESYNC_CHECKS_RELATIVE_DIR_PATH as cn, CONFLICTING_TARGET_PAIRS as ct, CODEBUDDY_DIR as d, RULESYNC_CONFIG_SCHEMA_URL as dn, SourceEntrySchema as dt, ALL_TOOL_TARGETS as en, RulesyncCheckFrontmatterSchema as et, CODEBUDDY_LOCAL_RULE_FILE_NAME as f, RULESYNC_CURATED_RULES_RELATIVE_DIR_PATH as fn, assertTargetsFeaturesExclusive as ft, McpProcessor as g, RULESYNC_HOOKS_RELATIVE_FILE_PATH as gn, WarningCollectingLogger as gt, shortenToWidth as h, RULESYNC_HOOKS_LEGACY_FILE_NAME as hn, JsonLogger as ht, inspectInputRoots as i, CURATED_RULES_FEATURE_SUBDIR as in, SKILL_FILE_NAME as it, FACTORYDROID_DIR as j, RULESYNC_SOURCES_LOCK_RELATIVE_FILE_PATH as jn, ensureDir as jt, CLAUDECODE_SETTINGS_LOCAL_FILE_NAME as k, RULESYNC_RULES_RELATIVE_DIR_PATH as kn, createTempDirectory as kt, SubagentsProcessor as l, RULESYNC_COMMANDS_RELATIVE_DIR_PATH as ln, ConfigFileSchema as lt, displayWidthOf as m, RULESYNC_HOOKS_FILE_NAME as mn, ConsoleLogger as mt, formatSourceLoadFailure as n, PACKAGING_TOOL_TARGETS as nn, loadYaml as nt, convertFromTool as o, RULESYNC_AIIGNORE_FILE_NAME as on, mergeInputRootConfigs as ot, ELLIPSIS_WIDTH as p, RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH as pn, findControlCharacter as pt, RulesyncHooks as q, removeFileStrict as qt, generate as r, ToolTargetSchema as rn, SHARED_USER_MANAGED_CONFIG_PATHS as rt, isPackagingToolTarget as s, RULESYNC_AIIGNORE_RELATIVE_FILE_PATH as sn, resolveEffectiveInputRoots as st, importFromTool as t, ALL_TOOL_TARGETS_WITH_WILDCARD as tn, stringifyFrontmatter as tt, SkillsProcessor as u, RULESYNC_CONFIG_RELATIVE_FILE_PATH as un, GITIGNORE_DESTINATION_KEY as ut, CRUSH_LOCAL_RULE_FILE_NAME as v, RULESYNC_LOCAL_CONFIG_RELATIVE_FILE_PATH as vn, warnOnConflictingFlags as vt, CODEXCLI_BASH_RULES_FILE_NAME as w, RULESYNC_PERMISSIONS_FILE_NAME as wn, applyFileMode as wt, QWENCODE_DIR as x, RULESYNC_MCP_RELATIVE_FILE_PATH as xn, withWarnOnceScope as xt, HooksProcessor as y, RULESYNC_MCP_FILE_NAME as yn, withFallbackLoggerTarget as yt, RulesyncSubagentFrontmatterSchema as z, formatError as zn, listFilePathsRecursively as zt };
|
|
71478
75027
|
|
|
71479
|
-
//# sourceMappingURL=import-
|
|
75028
|
+
//# sourceMappingURL=import-D2GhBXJV.js.map
|