rulesync 16.10.0 → 16.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/dist/cli/index.cjs +473 -113
- package/dist/cli/index.js +473 -113
- package/dist/cli/index.js.map +1 -1
- package/dist/{import-danhPI2x.cjs → import-BwdPcfzS.cjs} +337 -82
- package/dist/{import-CXJwVed1.js → import-zGCKgpdt.js} +338 -83
- package/dist/import-zGCKgpdt.js.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +2 -0
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/import-CXJwVed1.js.map +0 -1
|
@@ -891,6 +891,7 @@ const ErrorCodes = {
|
|
|
891
891
|
INIT_FAILED: "INIT_FAILED",
|
|
892
892
|
MCP_FAILED: "MCP_FAILED",
|
|
893
893
|
DOCTOR_FAILED: "DOCTOR_FAILED",
|
|
894
|
+
RELEASE_NOTES_FAILED: "RELEASE_NOTES_FAILED",
|
|
894
895
|
UNKNOWN_ERROR: "UNKNOWN_ERROR"
|
|
895
896
|
};
|
|
896
897
|
/**
|
|
@@ -2477,38 +2478,67 @@ const AMP_HOOK_EVENTS = [
|
|
|
2477
2478
|
];
|
|
2478
2479
|
/**
|
|
2479
2480
|
* Hook events supported by Cline's file-based hooks. Cline resolves one
|
|
2480
|
-
* executable per lifecycle event from its hooks directory, and the
|
|
2481
|
-
*
|
|
2482
|
-
*
|
|
2483
|
-
*
|
|
2484
|
-
* `
|
|
2481
|
+
* executable per lifecycle event from its hooks directory, and the accepted
|
|
2482
|
+
* event names come from two runtimes that read the same directory:
|
|
2483
|
+
*
|
|
2484
|
+
* - The VS Code extension fixes them in `VALID_HOOK_TYPES`
|
|
2485
|
+
* (`apps/vscode/src/core/hooks/utils.ts`): `TaskStart`, `TaskResume`,
|
|
2486
|
+
* `TaskCancel`, `TaskComplete`, `PreToolUse`, `PostToolUse`,
|
|
2487
|
+
* `UserPromptSubmit`, `Notification` and `PreCompact`.
|
|
2488
|
+
* - The SDK/CLI fixes them in `HookConfigFileName`
|
|
2489
|
+
* (`sdk/packages/core/src/hooks/hook-file-config.ts`), which drops
|
|
2490
|
+
* `Notification` but adds `TaskError` (→ `agent_error`) and
|
|
2491
|
+
* `SessionShutdown` (→ `session_shutdown`).
|
|
2492
|
+
*
|
|
2493
|
+
* This set is the union, because `.clinerules/hooks` is in both runtimes'
|
|
2494
|
+
* search paths and a script named for an event the running one does not know
|
|
2495
|
+
* is simply never spawned. That holds for unknown *names* only: for an event a
|
|
2496
|
+
* runtime does know, the SDK/CLI spawns both the extensionless script and its
|
|
2497
|
+
* `.ps1` twin, so each generated script opens with a guard that stands down on
|
|
2498
|
+
* the platform the other one owns — see `generateClineHookScript` and
|
|
2499
|
+
* `generateClineHookPowerShellScript`.
|
|
2485
2500
|
*
|
|
2486
2501
|
* `TaskResume` and `TaskCancel` have no canonical counterpart and stay
|
|
2487
2502
|
* unmapped rather than being approximated by `sessionEnd` / `stop`, whose
|
|
2488
2503
|
* semantics differ.
|
|
2489
2504
|
*
|
|
2490
2505
|
* @see https://github.com/cline/cline/blob/main/apps/vscode/src/core/hooks/utils.ts
|
|
2506
|
+
* @see https://github.com/cline/cline/blob/main/sdk/packages/core/src/hooks/hook-file-config.ts
|
|
2491
2507
|
*/
|
|
2492
2508
|
const CLINE_HOOK_EVENTS = [
|
|
2493
2509
|
"sessionStart",
|
|
2510
|
+
"sessionEnd",
|
|
2494
2511
|
"preToolUse",
|
|
2495
2512
|
"postToolUse",
|
|
2496
2513
|
"beforeSubmitPrompt",
|
|
2497
2514
|
"preCompact",
|
|
2498
2515
|
"notification",
|
|
2499
|
-
"taskCompleted"
|
|
2516
|
+
"taskCompleted",
|
|
2517
|
+
"afterError"
|
|
2500
2518
|
];
|
|
2501
2519
|
/**
|
|
2502
2520
|
* Hook events supported by GitHub Copilot (cloud coding agent).
|
|
2503
2521
|
*
|
|
2504
|
-
*
|
|
2522
|
+
* The events rulesync writes to `.github/hooks/*.json`:
|
|
2505
2523
|
* `sessionStart`, `sessionEnd`, `userPromptSubmitted` ← `beforeSubmitPrompt`,
|
|
2506
|
-
* `preToolUse`, `postToolUse`, `agentStop` ← `stop`,
|
|
2507
|
-
* `errorOccurred` ← `afterError
|
|
2508
|
-
*
|
|
2524
|
+
* `preToolUse`, `postToolUse`, `postToolUseFailure`, `agentStop` ← `stop`,
|
|
2525
|
+
* `subagentStart`, `subagentStop`, `errorOccurred` ← `afterError`,
|
|
2526
|
+
* `preCompact`, and `userPromptTransformed` ← `userPromptExpansion`.
|
|
2527
|
+
*
|
|
2528
|
+
* `preCompact` and `subagentStart` are authorable because the unified hooks
|
|
2529
|
+
* reference's per-event "Cloud agent" column says both fire there. That column
|
|
2530
|
+
* is the authority for this set: the older cloud-agent concept page still
|
|
2531
|
+
* lists only the eight events this set began as, and re-narrowing to it would
|
|
2532
|
+
* undo that. `notification` and `permissionRequest` stay out because the same
|
|
2533
|
+
* column is explicit that they do not fire on the cloud agent.
|
|
2534
|
+
*
|
|
2535
|
+
* `postToolUseFailure` and `userPromptTransformed` are shared with
|
|
2536
|
+
* {@link COPILOTCLI_HOOK_EVENTS}; the same column records both as firing on the
|
|
2537
|
+
* cloud agent, so they are authorable here too. The event surfaces overlap but
|
|
2538
|
+
* the config surfaces do not: `copilot` emits `command` hooks only, while the
|
|
2539
|
+
* CLI adapter also handles `http` and `prompt`.
|
|
2509
2540
|
*
|
|
2510
|
-
* @see https://docs.github.com/en/copilot/
|
|
2511
|
-
* @see https://docs.github.com/en/copilot/concepts/agents/hooks
|
|
2541
|
+
* @see https://docs.github.com/en/copilot/reference/hooks-reference
|
|
2512
2542
|
*/
|
|
2513
2543
|
const COPILOT_HOOK_EVENTS = [
|
|
2514
2544
|
"sessionStart",
|
|
@@ -2516,9 +2546,13 @@ const COPILOT_HOOK_EVENTS = [
|
|
|
2516
2546
|
"beforeSubmitPrompt",
|
|
2517
2547
|
"preToolUse",
|
|
2518
2548
|
"postToolUse",
|
|
2549
|
+
"postToolUseFailure",
|
|
2519
2550
|
"stop",
|
|
2551
|
+
"subagentStart",
|
|
2520
2552
|
"subagentStop",
|
|
2521
|
-
"afterError"
|
|
2553
|
+
"afterError",
|
|
2554
|
+
"preCompact",
|
|
2555
|
+
"userPromptExpansion"
|
|
2522
2556
|
];
|
|
2523
2557
|
/**
|
|
2524
2558
|
* Hook events supported by the GitHub Copilot CLI (`copilotcli-hooks.ts`).
|
|
@@ -3234,15 +3268,21 @@ const CANONICAL_TO_AMP_EVENT_NAMES = {
|
|
|
3234
3268
|
beforeSubmitPrompt: "agent.start",
|
|
3235
3269
|
stop: "agent.end"
|
|
3236
3270
|
};
|
|
3237
|
-
/**
|
|
3271
|
+
/**
|
|
3272
|
+
* Map canonical hook events to Cline's hook script file names — the union of
|
|
3273
|
+
* the VS Code extension's `VALID_HOOK_TYPES` and the SDK/CLI's
|
|
3274
|
+
* `HookConfigFileName`, see {@link CLINE_HOOK_EVENTS}.
|
|
3275
|
+
*/
|
|
3238
3276
|
const CANONICAL_TO_CLINE_EVENT_NAMES = {
|
|
3239
3277
|
sessionStart: "TaskStart",
|
|
3278
|
+
sessionEnd: "SessionShutdown",
|
|
3240
3279
|
preToolUse: "PreToolUse",
|
|
3241
3280
|
postToolUse: "PostToolUse",
|
|
3242
3281
|
beforeSubmitPrompt: "UserPromptSubmit",
|
|
3243
3282
|
preCompact: "PreCompact",
|
|
3244
3283
|
notification: "Notification",
|
|
3245
|
-
taskCompleted: "TaskComplete"
|
|
3284
|
+
taskCompleted: "TaskComplete",
|
|
3285
|
+
afterError: "TaskError"
|
|
3246
3286
|
};
|
|
3247
3287
|
/**
|
|
3248
3288
|
* Map canonical camelCase event names to Copilot camelCase.
|
|
@@ -3253,9 +3293,13 @@ const CANONICAL_TO_COPILOT_EVENT_NAMES = {
|
|
|
3253
3293
|
beforeSubmitPrompt: "userPromptSubmitted",
|
|
3254
3294
|
preToolUse: "preToolUse",
|
|
3255
3295
|
postToolUse: "postToolUse",
|
|
3296
|
+
postToolUseFailure: "postToolUseFailure",
|
|
3256
3297
|
stop: "agentStop",
|
|
3298
|
+
subagentStart: "subagentStart",
|
|
3257
3299
|
subagentStop: "subagentStop",
|
|
3258
|
-
afterError: "errorOccurred"
|
|
3300
|
+
afterError: "errorOccurred",
|
|
3301
|
+
preCompact: "preCompact",
|
|
3302
|
+
userPromptExpansion: "userPromptTransformed"
|
|
3259
3303
|
};
|
|
3260
3304
|
/**
|
|
3261
3305
|
* Map Copilot camelCase event names to canonical camelCase.
|
|
@@ -5480,6 +5524,7 @@ const RulesyncSkillFrontmatterSchema = zod_mini.z.looseObject({
|
|
|
5480
5524
|
})),
|
|
5481
5525
|
cline: zod_mini.z.optional(zod_mini.z.looseObject({})),
|
|
5482
5526
|
roo: zod_mini.z.optional(zod_mini.z.looseObject({})),
|
|
5527
|
+
amp: zod_mini.z.optional(zod_mini.z.looseObject({})),
|
|
5483
5528
|
devin: zod_mini.z.optional(zod_mini.z.looseObject({
|
|
5484
5529
|
"argument-hint": zod_mini.z.optional(zod_mini.z.string()),
|
|
5485
5530
|
model: zod_mini.z.optional(zod_mini.z.string()),
|
|
@@ -11712,7 +11757,8 @@ const KiloCommandFrontmatterSchema = zod_mini.z.looseObject({
|
|
|
11712
11757
|
description: zod_mini.z.optional(zod_mini.z.string()),
|
|
11713
11758
|
agent: zod_mini.z.optional(zod_mini.z.string()),
|
|
11714
11759
|
subtask: zod_mini.z.optional(zod_mini.z.boolean()),
|
|
11715
|
-
model: zod_mini.z.optional(zod_mini.z.string())
|
|
11760
|
+
model: zod_mini.z.optional(zod_mini.z.string()),
|
|
11761
|
+
variant: zod_mini.z.optional(zod_mini.z.string())
|
|
11716
11762
|
});
|
|
11717
11763
|
var KiloCommand = class KiloCommand extends ToolCommand {
|
|
11718
11764
|
frontmatter;
|
|
@@ -15394,6 +15440,13 @@ function generateClineHookScript({ event, commands }) {
|
|
|
15394
15440
|
`# ${event} hook generated by rulesync — edit .rulesync/hooks.jsonc and regenerate.`,
|
|
15395
15441
|
`# ${CLINE_HOOK_SCRIPT_MARKER}`,
|
|
15396
15442
|
"",
|
|
15443
|
+
"case \"${OSTYPE:-$(uname -s 2>/dev/null || true)}\" in",
|
|
15444
|
+
" msys*|MSYS*|cygwin*|CYGWIN*|MINGW*|mingw*)",
|
|
15445
|
+
` printf '{"cancel": false, "contextModification": "", "errorMessage": ""}\\n'`,
|
|
15446
|
+
" exit 0",
|
|
15447
|
+
" ;;",
|
|
15448
|
+
"esac",
|
|
15449
|
+
"",
|
|
15397
15450
|
"payload=$(cat)",
|
|
15398
15451
|
"cancel=false",
|
|
15399
15452
|
"error_message=''",
|
|
@@ -15407,15 +15460,39 @@ function generateClineHookScript({ event, commands }) {
|
|
|
15407
15460
|
return lines.join("\n");
|
|
15408
15461
|
}
|
|
15409
15462
|
/**
|
|
15410
|
-
* The PowerShell twin of {@link generateClineHookScript}. On Windows
|
|
15411
|
-
* resolves only `<Event>.ps1` and runs it through `powershell -File`,
|
|
15412
|
-
* spellings are written and the platform picks one.
|
|
15463
|
+
* The PowerShell twin of {@link generateClineHookScript}. On Windows the VS Code
|
|
15464
|
+
* extension resolves only `<Event>.ps1` and runs it through `powershell -File`,
|
|
15465
|
+
* so both spellings are written and the platform picks one.
|
|
15466
|
+
*
|
|
15467
|
+
* The SDK/CLI runtime does not pick one. `listHookConfigFiles` dedupes by path,
|
|
15468
|
+
* so `TaskError` and `TaskError.ps1` are two entries naming the same event, and
|
|
15469
|
+
* `createHookCommandMap` appends both to that event's command list without a
|
|
15470
|
+
* per-event dedupe — it then runs every command in the list, spawning a `.ps1`
|
|
15471
|
+
* through `pwsh` on Unix too. That produced noise rather than a second
|
|
15472
|
+
* execution (the body shells out through `cmd /c`, which Unix has no such
|
|
15473
|
+
* thing), but it is still a failure reported on every fire.
|
|
15474
|
+
*
|
|
15475
|
+
* Hence the leading platform guard: off Windows the script answers with the
|
|
15476
|
+
* neutral success payload and exits, leaving the POSIX twin to do the work.
|
|
15477
|
+
* Its counterpart at the top of {@link generateClineHookScript} covers the
|
|
15478
|
+
* quadrant where the duplication is real — Windows with a POSIX shell.
|
|
15479
|
+
* `$IsWindows` only exists in PowerShell 6+, and is `$null` under the Windows
|
|
15480
|
+
* PowerShell 5.1 that `powershell -File` starts — so the guard tests that it is
|
|
15481
|
+
* defined *and* false, rather than `-not $IsWindows`, which would be true on
|
|
15482
|
+
* 5.1 and would no-op the script on the one platform it exists for.
|
|
15483
|
+
*
|
|
15484
|
+
* @see https://github.com/cline/cline/blob/main/sdk/packages/core/src/hooks/hook-file-hooks.ts
|
|
15413
15485
|
*/
|
|
15414
15486
|
function generateClineHookPowerShellScript({ event, commands }) {
|
|
15415
15487
|
const lines = [
|
|
15416
15488
|
`# ${event} hook generated by rulesync — edit .rulesync/hooks.jsonc and regenerate.`,
|
|
15417
15489
|
`# ${CLINE_HOOK_SCRIPT_MARKER}`,
|
|
15418
15490
|
"",
|
|
15491
|
+
"if ($null -ne $IsWindows -and -not $IsWindows) {",
|
|
15492
|
+
` Write-Output '{"cancel": false, "contextModification": "", "errorMessage": ""}'`,
|
|
15493
|
+
" exit 0",
|
|
15494
|
+
"}",
|
|
15495
|
+
"",
|
|
15419
15496
|
"$payload = [Console]::In.ReadToEnd()",
|
|
15420
15497
|
"$cancel = $false",
|
|
15421
15498
|
"$errorMessage = ''",
|
|
@@ -15477,10 +15554,14 @@ var ClineHookScript = class extends ToolFile {
|
|
|
15477
15554
|
* by the contract, so every generated script carries a marker line and a script
|
|
15478
15555
|
* without it is never overwritten.
|
|
15479
15556
|
*
|
|
15480
|
-
*
|
|
15481
|
-
*
|
|
15557
|
+
* The project hooks directory is in the search paths of both the VS Code
|
|
15558
|
+
* extension and the SDK/CLI, whose accepted event names differ slightly, so the
|
|
15559
|
+
* emitted set is their union ({@link CANONICAL_TO_CLINE_EVENT_NAMES}). Cline's
|
|
15560
|
+
* in-process hook surface (`AgentHooks` from `@cline/core`) is a separate
|
|
15561
|
+
* mechanism this adapter does not target.
|
|
15482
15562
|
*
|
|
15483
15563
|
* @see https://github.com/cline/cline/blob/main/apps/vscode/src/core/hooks/utils.ts
|
|
15564
|
+
* @see https://github.com/cline/cline/blob/main/sdk/packages/core/src/hooks/hook-file-config.ts
|
|
15484
15565
|
*/
|
|
15485
15566
|
var ClineHooks = class ClineHooks extends ToolHooks {
|
|
15486
15567
|
scriptsByEvent;
|
|
@@ -15635,6 +15716,11 @@ const CODEXCLI_CONVERTER_CONFIG = {
|
|
|
15635
15716
|
numberPassthroughFields: [{
|
|
15636
15717
|
canonical: "additionalContextLimit",
|
|
15637
15718
|
tool: "additionalContextLimit"
|
|
15719
|
+
}],
|
|
15720
|
+
booleanPassthroughFields: [{
|
|
15721
|
+
canonical: "async",
|
|
15722
|
+
tool: "async",
|
|
15723
|
+
commandOnly: true
|
|
15638
15724
|
}]
|
|
15639
15725
|
};
|
|
15640
15726
|
/**
|
|
@@ -16761,6 +16847,16 @@ const FACTORYDROID_CONVERTER_CONFIG = {
|
|
|
16761
16847
|
subdividesGroup: true
|
|
16762
16848
|
}]
|
|
16763
16849
|
};
|
|
16850
|
+
/** Droid's nine event names, the keys a standalone `hooks.json` is made of. */
|
|
16851
|
+
const FACTORYDROID_EVENT_NAMES = new Set(Object.values(CANONICAL_TO_FACTORYDROID_EVENT_NAMES));
|
|
16852
|
+
/**
|
|
16853
|
+
* Whether a parsed hooks file is the standalone shape — keyed directly by event
|
|
16854
|
+
* name — rather than the `settings.json` shape that wraps the same map in a
|
|
16855
|
+
* `hooks` key.
|
|
16856
|
+
*/
|
|
16857
|
+
function hasFactorydroidEventKey(parsed) {
|
|
16858
|
+
return Object.keys(parsed).some((key) => FACTORYDROID_EVENT_NAMES.has(key));
|
|
16859
|
+
}
|
|
16764
16860
|
var FactorydroidHooks = class FactorydroidHooks extends ToolHooks {
|
|
16765
16861
|
constructor(params) {
|
|
16766
16862
|
super({
|
|
@@ -16788,14 +16884,6 @@ var FactorydroidHooks = class FactorydroidHooks extends ToolHooks {
|
|
|
16788
16884
|
}
|
|
16789
16885
|
static async fromRulesyncHooks({ outputRoot = process.cwd(), rulesyncHooks, validate = true, global = false, logger }) {
|
|
16790
16886
|
const paths = FactorydroidHooks.getSettablePaths({ global });
|
|
16791
|
-
const filePath = (0, node_path.join)(outputRoot, paths.relativeDirPath, paths.relativeFilePath);
|
|
16792
|
-
const existingContent = await readFileContentOrNull(filePath) ?? JSON.stringify({}, null, 2);
|
|
16793
|
-
let settings;
|
|
16794
|
-
try {
|
|
16795
|
-
settings = JSON.parse(existingContent);
|
|
16796
|
-
} catch (error) {
|
|
16797
|
-
throw new Error(`Failed to parse existing Factory Droid hooks file at ${filePath}: ${formatError(error)}`, { cause: error });
|
|
16798
|
-
}
|
|
16799
16887
|
const config = rulesyncHooks.getJson();
|
|
16800
16888
|
const factorydroidHooks = canonicalToToolHooks({
|
|
16801
16889
|
config,
|
|
@@ -16803,11 +16891,7 @@ var FactorydroidHooks = class FactorydroidHooks extends ToolHooks {
|
|
|
16803
16891
|
converterConfig: FACTORYDROID_CONVERTER_CONFIG,
|
|
16804
16892
|
logger
|
|
16805
16893
|
});
|
|
16806
|
-
const
|
|
16807
|
-
...settings,
|
|
16808
|
-
hooks: factorydroidHooks
|
|
16809
|
-
};
|
|
16810
|
-
const fileContent = JSON.stringify(merged, null, 2);
|
|
16894
|
+
const fileContent = JSON.stringify(factorydroidHooks, null, 2);
|
|
16811
16895
|
return new FactorydroidHooks({
|
|
16812
16896
|
outputRoot,
|
|
16813
16897
|
relativeDirPath: paths.relativeDirPath,
|
|
@@ -16817,14 +16901,14 @@ var FactorydroidHooks = class FactorydroidHooks extends ToolHooks {
|
|
|
16817
16901
|
});
|
|
16818
16902
|
}
|
|
16819
16903
|
toRulesyncHooks({ logger } = {}) {
|
|
16820
|
-
let
|
|
16904
|
+
let parsed;
|
|
16821
16905
|
try {
|
|
16822
|
-
|
|
16906
|
+
parsed = JSON.parse(this.getFileContent());
|
|
16823
16907
|
} catch (error) {
|
|
16824
16908
|
throw new Error(`Failed to parse Factory Droid hooks content in ${(0, node_path.join)(this.getRelativeDirPath(), this.getRelativeFilePath())}: ${formatError(error)}`, { cause: error });
|
|
16825
16909
|
}
|
|
16826
16910
|
const hooks = toolHooksToCanonical({
|
|
16827
|
-
hooks:
|
|
16911
|
+
hooks: hasFactorydroidEventKey(parsed) ? parsed : parsed.hooks,
|
|
16828
16912
|
converterConfig: FACTORYDROID_CONVERTER_CONFIG,
|
|
16829
16913
|
logger
|
|
16830
16914
|
});
|
|
@@ -16959,18 +17043,7 @@ const GROKCLI_CONVERTER_CONFIG = {
|
|
|
16959
17043
|
tool: "env",
|
|
16960
17044
|
commandOnly: true
|
|
16961
17045
|
}],
|
|
16962
|
-
noMatcherEvents: /* @__PURE__ */ new Set([
|
|
16963
|
-
"sessionStart",
|
|
16964
|
-
"sessionEnd",
|
|
16965
|
-
"beforeSubmitPrompt",
|
|
16966
|
-
"stop",
|
|
16967
|
-
"stopFailure",
|
|
16968
|
-
"notification",
|
|
16969
|
-
"subagentStart",
|
|
16970
|
-
"subagentStop",
|
|
16971
|
-
"preCompact",
|
|
16972
|
-
"postCompact"
|
|
16973
|
-
])
|
|
17046
|
+
noMatcherEvents: /* @__PURE__ */ new Set(["stop", "beforeSubmitPrompt"])
|
|
16974
17047
|
};
|
|
16975
17048
|
/**
|
|
16976
17049
|
* Hooks generator for Grok CLI (xAI Grok Build).
|
|
@@ -17070,6 +17143,13 @@ var GrokcliHooks = class GrokcliHooks extends ToolHooks {
|
|
|
17070
17143
|
* @see https://github.com/NousResearch/hermes-agent/blob/main/website/docs/user-guide/features/hooks.md
|
|
17071
17144
|
*/
|
|
17072
17145
|
const HERMESAGENT_MATCHER_EVENTS = /* @__PURE__ */ new Set(["pre_tool_call", "post_tool_call"]);
|
|
17146
|
+
/**
|
|
17147
|
+
* The only Hermes event that can block, and therefore the only one whose
|
|
17148
|
+
* entries accept `fail_closed`. Upstream warns and ignores the key on every
|
|
17149
|
+
* other event ("only blocking-capable events can fail closed").
|
|
17150
|
+
* @see https://github.com/NousResearch/hermes-agent/blob/main/website/docs/user-guide/features/hooks.md
|
|
17151
|
+
*/
|
|
17152
|
+
const HERMESAGENT_FAIL_CLOSED_EVENT = "pre_tool_call";
|
|
17073
17153
|
const HERMESAGENT_CANONICAL_EVENTS = new Set(HERMESAGENT_HOOK_EVENTS);
|
|
17074
17154
|
const HERMESAGENT_NATIVE_EVENTS = new Set(HERMESAGENT_NATIVE_HOOK_EVENTS);
|
|
17075
17155
|
/**
|
|
@@ -17103,7 +17183,9 @@ function isHermesHookEventEntry(key, value) {
|
|
|
17103
17183
|
* unsupported hook types centrally). `matcher` is only carried through for
|
|
17104
17184
|
* `pre_tool_call`/`post_tool_call`; on any other event it is dropped with a
|
|
17105
17185
|
* warning, mirroring how other adapters (e.g. AugmentCode) handle
|
|
17106
|
-
* matcher-less lifecycle events.
|
|
17186
|
+
* matcher-less lifecycle events. The canonical `failClosed` field (shared with
|
|
17187
|
+
* Cursor, whose semantics Hermes copied) becomes `fail_closed`, which upstream
|
|
17188
|
+
* only honours on `pre_tool_call`.
|
|
17107
17189
|
*/
|
|
17108
17190
|
function definitionsToHermesEntries({ event, sourceEvent = event, definitions, logger }) {
|
|
17109
17191
|
const supportsMatcher = HERMESAGENT_MATCHER_EVENTS.has(event);
|
|
@@ -17114,6 +17196,10 @@ function definitionsToHermesEntries({ event, sourceEvent = event, definitions, l
|
|
|
17114
17196
|
if (typeof definition.matcher === "string" && definition.matcher !== "") if (supportsMatcher) entry.matcher = definition.matcher;
|
|
17115
17197
|
else logger?.warn(`matcher "${definition.matcher}" on "${sourceEvent}" hook will be ignored — Hermes Agent only supports matchers on pre_tool_call/post_tool_call`);
|
|
17116
17198
|
if (typeof definition.timeout === "number") entry.timeout = definition.timeout;
|
|
17199
|
+
if (typeof definition.failClosed === "boolean") {
|
|
17200
|
+
if (event === HERMESAGENT_FAIL_CLOSED_EVENT) entry.fail_closed = definition.failClosed;
|
|
17201
|
+
else if (definition.failClosed) logger?.warn(`failClosed on "${sourceEvent}" hook will be ignored — Hermes Agent only supports fail_closed on pre_tool_call`);
|
|
17202
|
+
}
|
|
17117
17203
|
entries.push(entry);
|
|
17118
17204
|
}
|
|
17119
17205
|
return entries;
|
|
@@ -17165,6 +17251,37 @@ function canonicalToHermesHooks({ config, toolOverrideHooks, logger }) {
|
|
|
17165
17251
|
return result;
|
|
17166
17252
|
}
|
|
17167
17253
|
/**
|
|
17254
|
+
* Reads a hook entry's fail-closed flag. Upstream accepts its own `fail_closed`
|
|
17255
|
+
* and the Cursor/Claude Code `failClosed` spelling alike, so both have to be
|
|
17256
|
+
* read back into the single canonical field.
|
|
17257
|
+
*/
|
|
17258
|
+
function readHermesFailClosed(entry) {
|
|
17259
|
+
if (typeof entry.fail_closed === "boolean") return entry.fail_closed;
|
|
17260
|
+
if (typeof entry.failClosed === "boolean") return entry.failClosed;
|
|
17261
|
+
}
|
|
17262
|
+
/**
|
|
17263
|
+
* Converts one serialized Hermes hook entry back into a canonical definition,
|
|
17264
|
+
* or `undefined` when it is not a hook rulesync models (no string `command`).
|
|
17265
|
+
* `matcher` and `fail_closed` are read only on the events upstream honours them
|
|
17266
|
+
* on, so an imported value is never one the next generate warns about and drops.
|
|
17267
|
+
*/
|
|
17268
|
+
function hermesEntryToDefinition({ nativeEvent, raw }) {
|
|
17269
|
+
if (!isRecord$1(raw)) return;
|
|
17270
|
+
const entry = raw;
|
|
17271
|
+
if (typeof entry.command !== "string") return;
|
|
17272
|
+
const def = {
|
|
17273
|
+
type: "command",
|
|
17274
|
+
command: entry.command
|
|
17275
|
+
};
|
|
17276
|
+
if (HERMESAGENT_MATCHER_EVENTS.has(nativeEvent) && typeof entry.matcher === "string" && entry.matcher !== "") def.matcher = entry.matcher;
|
|
17277
|
+
if (typeof entry.timeout === "number") def.timeout = entry.timeout;
|
|
17278
|
+
if (nativeEvent === HERMESAGENT_FAIL_CLOSED_EVENT) {
|
|
17279
|
+
const failClosed = readHermesFailClosed(entry);
|
|
17280
|
+
if (failClosed !== void 0) def.failClosed = failClosed;
|
|
17281
|
+
}
|
|
17282
|
+
return def;
|
|
17283
|
+
}
|
|
17284
|
+
/**
|
|
17168
17285
|
* Reverse {@link canonicalToHermesHooks}: parse Hermes's native
|
|
17169
17286
|
* `hooks: { <event>: [...] }` map back into a canonical event → definition[]
|
|
17170
17287
|
* record. Native events with no canonical equivalent (`pre_verify`,
|
|
@@ -17178,19 +17295,10 @@ function hermesHooksToCanonical(hooks) {
|
|
|
17178
17295
|
if (PROTOTYPE_POLLUTION_KEYS.has(nativeEvent) || !Array.isArray(entries)) continue;
|
|
17179
17296
|
if (!isHermesHookEventEntry(nativeEvent, entries)) continue;
|
|
17180
17297
|
const rulesyncEvent = HERMESAGENT_TO_CANONICAL_EVENT_NAMES[nativeEvent] ?? nativeEvent;
|
|
17181
|
-
const defs =
|
|
17182
|
-
|
|
17183
|
-
|
|
17184
|
-
|
|
17185
|
-
if (typeof entry.command !== "string") continue;
|
|
17186
|
-
const def = {
|
|
17187
|
-
type: "command",
|
|
17188
|
-
command: entry.command
|
|
17189
|
-
};
|
|
17190
|
-
if (HERMESAGENT_MATCHER_EVENTS.has(nativeEvent) && typeof entry.matcher === "string" && entry.matcher !== "") def.matcher = entry.matcher;
|
|
17191
|
-
if (typeof entry.timeout === "number") def.timeout = entry.timeout;
|
|
17192
|
-
defs.push(def);
|
|
17193
|
-
}
|
|
17298
|
+
const defs = entries.map((raw) => hermesEntryToDefinition({
|
|
17299
|
+
nativeEvent,
|
|
17300
|
+
raw
|
|
17301
|
+
})).filter((def) => def !== void 0);
|
|
17194
17302
|
if (defs.length > 0) canonical[rulesyncEvent] = defs;
|
|
17195
17303
|
}
|
|
17196
17304
|
return canonical;
|
|
@@ -23116,6 +23224,78 @@ var CursorMcp = class CursorMcp extends ToolMcp {
|
|
|
23116
23224
|
};
|
|
23117
23225
|
//#endregion
|
|
23118
23226
|
//#region src/features/mcp/deepagents-mcp.ts
|
|
23227
|
+
const TOOL_NAME = "deepagents";
|
|
23228
|
+
/**
|
|
23229
|
+
* Map a canonical transport onto the three dcode accepts.
|
|
23230
|
+
*
|
|
23231
|
+
* `_resolve_server_type` takes `stdio`, `sse` and `http`, plus the aliases
|
|
23232
|
+
* `streamable_http` / `streamable-http` → `http`. Rulesync's canonical
|
|
23233
|
+
* vocabulary is wider: `local` and `ws` are spellings dcode rejects outright,
|
|
23234
|
+
* and a rejected server is dropped at load time with only a log line. `local`
|
|
23235
|
+
* has an exact equivalent so it is translated; `ws` has none and is skipped at
|
|
23236
|
+
* generate time instead, where the warning can still reach the author.
|
|
23237
|
+
*
|
|
23238
|
+
* @see https://docs.langchain.com/oss/deepagents/code/mcp-tools
|
|
23239
|
+
*/
|
|
23240
|
+
function normalizeDeepagentsTransport(transport) {
|
|
23241
|
+
switch (transport) {
|
|
23242
|
+
case "local":
|
|
23243
|
+
case "stdio": return "stdio";
|
|
23244
|
+
case "streamable-http":
|
|
23245
|
+
case "streamable_http":
|
|
23246
|
+
case "http": return "http";
|
|
23247
|
+
case "sse": return "sse";
|
|
23248
|
+
default: return;
|
|
23249
|
+
}
|
|
23250
|
+
}
|
|
23251
|
+
/**
|
|
23252
|
+
* Translate one canonical server into dcode's `.mcp.json` shape, or `null` to
|
|
23253
|
+
* skip it.
|
|
23254
|
+
*
|
|
23255
|
+
* Two upstream constraints from `_validate_tool_filter_fields` are enforced
|
|
23256
|
+
* here, because breaking either one makes dcode drop the whole server: the two
|
|
23257
|
+
* filters are mutually exclusive on a single server, and neither may be an
|
|
23258
|
+
* empty list.
|
|
23259
|
+
*/
|
|
23260
|
+
function toDeepagentsServer({ name, server, logger }) {
|
|
23261
|
+
const rawTransport = server.transport ?? server.type;
|
|
23262
|
+
if (rawTransport === "ws") return warnAndSkipMcpServer({
|
|
23263
|
+
toolName: TOOL_NAME,
|
|
23264
|
+
serverName: name,
|
|
23265
|
+
reason: "the WebSocket transport, which deepagents does not support",
|
|
23266
|
+
logger
|
|
23267
|
+
});
|
|
23268
|
+
const { enabledTools, disabledTools, type: _type, transport, ...rest } = server;
|
|
23269
|
+
const converted = { ...rest };
|
|
23270
|
+
const normalized = normalizeDeepagentsTransport(rawTransport);
|
|
23271
|
+
if (normalized !== void 0) if (transport !== void 0) converted.transport = normalized;
|
|
23272
|
+
else converted.type = normalized;
|
|
23273
|
+
if (enabledTools !== void 0 && disabledTools !== void 0) return warnAndSkipMcpServer({
|
|
23274
|
+
toolName: TOOL_NAME,
|
|
23275
|
+
serverName: name,
|
|
23276
|
+
reason: "both enabledTools and disabledTools, which deepagents rejects — pick one",
|
|
23277
|
+
logger
|
|
23278
|
+
});
|
|
23279
|
+
if (enabledTools !== void 0) {
|
|
23280
|
+
if (enabledTools.length === 0) return warnAndSkipMcpServer({
|
|
23281
|
+
toolName: TOOL_NAME,
|
|
23282
|
+
serverName: name,
|
|
23283
|
+
reason: "an empty enabledTools list, which allows no tools at all and which deepagents rejects",
|
|
23284
|
+
logger
|
|
23285
|
+
});
|
|
23286
|
+
converted.allowedTools = enabledTools;
|
|
23287
|
+
} else if (disabledTools !== void 0) if (disabledTools.length === 0) logger?.warn(`${TOOL_NAME} MCP: dropping the empty disabledTools list on "${name}"; it denies nothing, and deepagents rejects the empty form.`);
|
|
23288
|
+
else converted.disabledTools = disabledTools;
|
|
23289
|
+
return converted;
|
|
23290
|
+
}
|
|
23291
|
+
/** Lift dcode's spellings back into the canonical model. */
|
|
23292
|
+
function toRulesyncServer(server) {
|
|
23293
|
+
const { allowedTools, ...rest } = server;
|
|
23294
|
+
const converted = { ...rest };
|
|
23295
|
+
for (const key of ["type", "transport"]) if (converted[key] === "streamable_http" || converted[key] === "streamable-http") converted[key] = "http";
|
|
23296
|
+
if (Array.isArray(allowedTools)) converted.enabledTools = allowedTools;
|
|
23297
|
+
return converted;
|
|
23298
|
+
}
|
|
23119
23299
|
var DeepagentsMcp = class DeepagentsMcp extends ToolMcp {
|
|
23120
23300
|
json;
|
|
23121
23301
|
constructor(params) {
|
|
@@ -23150,12 +23330,22 @@ var DeepagentsMcp = class DeepagentsMcp extends ToolMcp {
|
|
|
23150
23330
|
validate
|
|
23151
23331
|
});
|
|
23152
23332
|
}
|
|
23153
|
-
static async fromRulesyncMcp({ outputRoot = process.cwd(), rulesyncMcp, validate = true, global = false }) {
|
|
23333
|
+
static async fromRulesyncMcp({ outputRoot = process.cwd(), rulesyncMcp, validate = true, global = false, logger }) {
|
|
23154
23334
|
const paths = this.getSettablePaths({ global });
|
|
23155
23335
|
const fileContent = await readFileContentOrNull((0, node_path.join)(outputRoot, paths.relativeDirPath, paths.relativeFilePath)) ?? JSON.stringify({ mcpServers: {} }, null, 2);
|
|
23336
|
+
const json = JSON.parse(fileContent);
|
|
23337
|
+
const mcpServers = {};
|
|
23338
|
+
for (const [name, server] of Object.entries(rulesyncMcp.getMcpServers())) {
|
|
23339
|
+
const converted = toDeepagentsServer({
|
|
23340
|
+
name,
|
|
23341
|
+
server,
|
|
23342
|
+
logger
|
|
23343
|
+
});
|
|
23344
|
+
if (converted !== null) mcpServers[name] = converted;
|
|
23345
|
+
}
|
|
23156
23346
|
const mcpJson = {
|
|
23157
|
-
...
|
|
23158
|
-
mcpServers
|
|
23347
|
+
...json,
|
|
23348
|
+
mcpServers
|
|
23159
23349
|
};
|
|
23160
23350
|
return new DeepagentsMcp({
|
|
23161
23351
|
outputRoot,
|
|
@@ -23166,7 +23356,9 @@ var DeepagentsMcp = class DeepagentsMcp extends ToolMcp {
|
|
|
23166
23356
|
});
|
|
23167
23357
|
}
|
|
23168
23358
|
toRulesyncMcp() {
|
|
23169
|
-
|
|
23359
|
+
const servers = isRecord$1(this.json.mcpServers) ? this.json.mcpServers : {};
|
|
23360
|
+
const mcpServers = Object.fromEntries(Object.entries(servers).map(([name, server]) => [name, isRecord$1(server) ? toRulesyncServer(server) : server]));
|
|
23361
|
+
return this.toRulesyncMcpDefault({ fileContent: JSON.stringify({ mcpServers }, null, 2) });
|
|
23170
23362
|
}
|
|
23171
23363
|
validate() {
|
|
23172
23364
|
return {
|
|
@@ -23871,7 +24063,7 @@ function resolveHermesTimeout(config) {
|
|
|
23871
24063
|
* alias — `auth` (`oauth` for OAuth 2.1/PKCE), mTLS `client_cert` (string PEM
|
|
23872
24064
|
* path, or `[cert, key]`/`[cert, key, password]` list) and `client_key`,
|
|
23873
24065
|
* `connect_timeout` (seconds), `supports_parallel_tool_calls`,
|
|
23874
|
-
* `keepalive_interval`, and `
|
|
24066
|
+
* `keepalive_interval`, `elicitation`, `trust`, and `identity_header` — verbatim
|
|
23875
24067
|
* from `source` to `target`. Field names are identical on both sides (the
|
|
23876
24068
|
* canonical `McpServerSchema` is a `looseObject`), so this serves export and
|
|
23877
24069
|
* import alike. See the Hermes mcp-config-reference.
|
|
@@ -23940,6 +24132,14 @@ function copyHermesAdvancedFields(source, target) {
|
|
|
23940
24132
|
target.elicitation = omitPrototypePollutionKeys(structuredClone(source.elicitation));
|
|
23941
24133
|
copied = true;
|
|
23942
24134
|
}
|
|
24135
|
+
if (typeof source.trust === "string") {
|
|
24136
|
+
target.trust = source.trust;
|
|
24137
|
+
copied = true;
|
|
24138
|
+
}
|
|
24139
|
+
if (isPlainObject$1(source.identity_header)) {
|
|
24140
|
+
target.identity_header = omitPrototypePollutionKeys(structuredClone(source.identity_header));
|
|
24141
|
+
copied = true;
|
|
24142
|
+
}
|
|
23943
24143
|
return copied;
|
|
23944
24144
|
}
|
|
23945
24145
|
/**
|
|
@@ -27173,8 +27373,8 @@ const toolMcpFactories = /* @__PURE__ */ new Map([
|
|
|
27173
27373
|
meta: {
|
|
27174
27374
|
supportsProject: true,
|
|
27175
27375
|
supportsGlobal: true,
|
|
27176
|
-
supportsEnabledTools:
|
|
27177
|
-
supportsDisabledTools:
|
|
27376
|
+
supportsEnabledTools: true,
|
|
27377
|
+
supportsDisabledTools: true
|
|
27178
27378
|
}
|
|
27179
27379
|
}],
|
|
27180
27380
|
["factorydroid", {
|
|
@@ -27183,7 +27383,7 @@ const toolMcpFactories = /* @__PURE__ */ new Map([
|
|
|
27183
27383
|
supportsProject: true,
|
|
27184
27384
|
supportsGlobal: true,
|
|
27185
27385
|
supportsEnabledTools: false,
|
|
27186
|
-
supportsDisabledTools:
|
|
27386
|
+
supportsDisabledTools: true
|
|
27187
27387
|
}
|
|
27188
27388
|
}],
|
|
27189
27389
|
["goose", {
|
|
@@ -27309,7 +27509,7 @@ const toolMcpFactories = /* @__PURE__ */ new Map([
|
|
|
27309
27509
|
supportsProject: true,
|
|
27310
27510
|
supportsGlobal: false,
|
|
27311
27511
|
supportsEnabledTools: false,
|
|
27312
|
-
supportsDisabledTools:
|
|
27512
|
+
supportsDisabledTools: true
|
|
27313
27513
|
}
|
|
27314
27514
|
}],
|
|
27315
27515
|
["zoocode", {
|
|
@@ -27318,7 +27518,7 @@ const toolMcpFactories = /* @__PURE__ */ new Map([
|
|
|
27318
27518
|
supportsProject: true,
|
|
27319
27519
|
supportsGlobal: false,
|
|
27320
27520
|
supportsEnabledTools: false,
|
|
27321
|
-
supportsDisabledTools:
|
|
27521
|
+
supportsDisabledTools: true
|
|
27322
27522
|
}
|
|
27323
27523
|
}],
|
|
27324
27524
|
["rovodev", {
|
|
@@ -31245,6 +31445,7 @@ function convertGoosePermissionConfigToRulesync(userPermission) {
|
|
|
31245
31445
|
const GROKCLI_UI_KEY = "ui";
|
|
31246
31446
|
const GROKCLI_PERMISSION_MODE_KEY = "permission_mode";
|
|
31247
31447
|
const GROKCLI_PERMISSION_KEY = "permission";
|
|
31448
|
+
const GROKCLI_AUTO_PERMISSION_MODE = "auto";
|
|
31248
31449
|
const CATCH_ALL_PATTERN$2 = "*";
|
|
31249
31450
|
const MCP_CANONICAL_PREFIX$1 = "mcp__";
|
|
31250
31451
|
const CATEGORY_TO_GROK_TOOL = {
|
|
@@ -31458,8 +31659,9 @@ var GrokcliPermissions = class GrokcliPermissions extends ToolPermissions {
|
|
|
31458
31659
|
deny: buckets.deny,
|
|
31459
31660
|
ask: buckets.ask
|
|
31460
31661
|
};
|
|
31461
|
-
const
|
|
31462
|
-
|
|
31662
|
+
const existingUi = isRecord$1(parsed[GROKCLI_UI_KEY]) ? parsed[GROKCLI_UI_KEY] : {};
|
|
31663
|
+
const uiPatch = global && existingUi[GROKCLI_PERMISSION_MODE_KEY] !== GROKCLI_AUTO_PERMISSION_MODE ? { [GROKCLI_UI_KEY]: {
|
|
31664
|
+
...existingUi,
|
|
31463
31665
|
[GROKCLI_PERMISSION_MODE_KEY]: deriveGrokPermissionMode(config)
|
|
31464
31666
|
} } : {};
|
|
31465
31667
|
return new GrokcliPermissions({
|
|
@@ -36744,11 +36946,12 @@ var AmpSkill = class AmpSkill extends ToolSkill {
|
|
|
36744
36946
|
};
|
|
36745
36947
|
}
|
|
36746
36948
|
toRulesyncSkill() {
|
|
36747
|
-
const
|
|
36949
|
+
const { name, description, ...ampSection } = this.getFrontmatter();
|
|
36748
36950
|
const rulesyncFrontmatter = {
|
|
36749
|
-
name
|
|
36750
|
-
description
|
|
36751
|
-
targets: ["*"]
|
|
36951
|
+
name,
|
|
36952
|
+
description,
|
|
36953
|
+
targets: ["*"],
|
|
36954
|
+
...Object.keys(ampSection).length > 0 && { amp: ampSection }
|
|
36752
36955
|
};
|
|
36753
36956
|
return new RulesyncSkill({
|
|
36754
36957
|
outputRoot: this.outputRoot,
|
|
@@ -36765,6 +36968,7 @@ var AmpSkill = class AmpSkill extends ToolSkill {
|
|
|
36765
36968
|
const settablePaths = AmpSkill.getSettablePaths({ global });
|
|
36766
36969
|
const rulesyncFrontmatter = rulesyncSkill.getFrontmatter();
|
|
36767
36970
|
const ampFrontmatter = {
|
|
36971
|
+
...rulesyncFrontmatter.amp,
|
|
36768
36972
|
name: rulesyncFrontmatter.name,
|
|
36769
36973
|
description: rulesyncFrontmatter.description
|
|
36770
36974
|
};
|
|
@@ -39191,6 +39395,47 @@ const JunieSkillFrontmatterSchema = zod_mini.z.looseObject({
|
|
|
39191
39395
|
name: zod_mini.z.string(),
|
|
39192
39396
|
description: zod_mini.z.string()
|
|
39193
39397
|
});
|
|
39398
|
+
/** An ATX markdown heading line (`#` … `######`). */
|
|
39399
|
+
const HEADING_LINE = /^#{1,6}(\s|$)/;
|
|
39400
|
+
/**
|
|
39401
|
+
* Junie's own fallback for a `SKILL.md` with no `description`: "If
|
|
39402
|
+
* `description` is not provided in the frontmatter, Junie CLI extracts the
|
|
39403
|
+
* first paragraph of the body content as the description." Headings do not
|
|
39404
|
+
* count as that paragraph — "If the body is also empty or contains only
|
|
39405
|
+
* headings, the skill will fail to load."
|
|
39406
|
+
*
|
|
39407
|
+
* This is import-only. The canonical `RulesyncSkillFrontmatter` requires a
|
|
39408
|
+
* description, so without the fallback a skill Junie itself loads fine aborts
|
|
39409
|
+
* the whole import; generation keeps emitting an explicit description, which
|
|
39410
|
+
* the same docs recommend.
|
|
39411
|
+
*
|
|
39412
|
+
* Heading lines are therefore skipped rather than taken: a body opening with
|
|
39413
|
+
* `# Skill Name` would otherwise import that title as the description and —
|
|
39414
|
+
* because the next generate writes it out explicitly — replace Junie's own
|
|
39415
|
+
* correct fallback with the wrong value everywhere, canonical config included.
|
|
39416
|
+
*
|
|
39417
|
+
* A paragraph runs to the first blank line or heading, and is collapsed onto
|
|
39418
|
+
* one line because it becomes a YAML frontmatter value. A fenced code block is
|
|
39419
|
+
* not treated specially: it is ordinary content, so a body whose first
|
|
39420
|
+
* paragraph is a fence yields the fence text. Returns an empty string when the
|
|
39421
|
+
* body holds no such paragraph, which the caller turns into a skipped skill.
|
|
39422
|
+
*
|
|
39423
|
+
* @see https://junie.jetbrains.com/docs/agent-skills.html
|
|
39424
|
+
*/
|
|
39425
|
+
function deriveDescriptionFromBody(body) {
|
|
39426
|
+
const paragraph = [];
|
|
39427
|
+
for (const rawLine of body.split(/\r?\n/)) {
|
|
39428
|
+
const line = rawLine.trim();
|
|
39429
|
+
if (paragraph.length === 0) {
|
|
39430
|
+
if (line === "" || HEADING_LINE.test(line)) continue;
|
|
39431
|
+
paragraph.push(line);
|
|
39432
|
+
continue;
|
|
39433
|
+
}
|
|
39434
|
+
if (line === "" || HEADING_LINE.test(line)) break;
|
|
39435
|
+
paragraph.push(line);
|
|
39436
|
+
}
|
|
39437
|
+
return paragraph.join(" ").trim();
|
|
39438
|
+
}
|
|
39194
39439
|
/**
|
|
39195
39440
|
* Represents a JetBrains Junie skill directory.
|
|
39196
39441
|
* Skills are stored under the .junie/skills directory with SKILL.md files.
|
|
@@ -39287,7 +39532,16 @@ var JunieSkill = class JunieSkill extends ToolSkill {
|
|
|
39287
39532
|
...params,
|
|
39288
39533
|
getSettablePaths: JunieSkill.getSettablePaths
|
|
39289
39534
|
});
|
|
39290
|
-
|
|
39535
|
+
let frontmatter = loaded.frontmatter;
|
|
39536
|
+
if (isRecord$1(frontmatter) && frontmatter.description === void 0) {
|
|
39537
|
+
const derived = deriveDescriptionFromBody(loaded.body);
|
|
39538
|
+
if (derived === "") throw new Error(`Cannot import ${(0, node_path.join)(loaded.outputRoot, loaded.relativeDirPath, loaded.dirName, SKILL_FILE_NAME)}: it has no description and its body has no paragraph to derive one from, so Junie cannot load it either. Add a description to the frontmatter.`);
|
|
39539
|
+
frontmatter = {
|
|
39540
|
+
...frontmatter,
|
|
39541
|
+
description: derived
|
|
39542
|
+
};
|
|
39543
|
+
}
|
|
39544
|
+
const result = JunieSkillFrontmatterSchema.safeParse(frontmatter);
|
|
39291
39545
|
if (!result.success) {
|
|
39292
39546
|
const skillDirPath = (0, node_path.join)(loaded.outputRoot, loaded.relativeDirPath, loaded.dirName);
|
|
39293
39547
|
throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME)}: ${formatError(result.error)}`);
|
|
@@ -41752,7 +42006,8 @@ const toolSkillFactories = /* @__PURE__ */ new Map([
|
|
|
41752
42006
|
meta: {
|
|
41753
42007
|
supportsProject: true,
|
|
41754
42008
|
supportsSimulated: false,
|
|
41755
|
-
supportsGlobal: true
|
|
42009
|
+
supportsGlobal: true,
|
|
42010
|
+
lenientImport: true
|
|
41756
42011
|
}
|
|
41757
42012
|
}],
|
|
41758
42013
|
["kilo", {
|