rulesync 16.28.1 → 16.30.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 +1 -0
- package/dist/cli/index.cjs +10 -5
- package/dist/cli/index.js +10 -5
- package/dist/cli/index.js.map +1 -1
- package/dist/{import-CwS7XtJP.js → import-BDlmyTcr.js} +746 -104
- package/dist/import-BDlmyTcr.js.map +1 -0
- package/dist/{import-hiWvR9cE.cjs → import-WJZi59fO.cjs} +762 -120
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +15 -1
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +15 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/import-CwS7XtJP.js.map +0 -1
|
@@ -444,34 +444,34 @@ const isFeatureValueEnabled = (value) => {
|
|
|
444
444
|
const parseCommaSeparatedList = (value) => value.split(",").map((s) => s.trim()).filter(Boolean);
|
|
445
445
|
//#endregion
|
|
446
446
|
//#region src/constants/rulesync-paths.ts
|
|
447
|
-
const { join: join$
|
|
447
|
+
const { join: join$310 } = node_path.posix;
|
|
448
448
|
const RULESYNC_CONFIG_RELATIVE_FILE_PATH = "rulesync.jsonc";
|
|
449
449
|
const RULESYNC_LOCAL_CONFIG_RELATIVE_FILE_PATH = "rulesync.local.jsonc";
|
|
450
450
|
const RULESYNC_RELATIVE_DIR_PATH = ".rulesync";
|
|
451
451
|
const RULES_FEATURE_SUBDIR = "rules";
|
|
452
|
-
const CURATED_RULES_FEATURE_SUBDIR = join$
|
|
452
|
+
const CURATED_RULES_FEATURE_SUBDIR = join$310(RULES_FEATURE_SUBDIR, ".curated");
|
|
453
453
|
const COMMANDS_FEATURE_SUBDIR = "commands";
|
|
454
454
|
const SUBAGENTS_FEATURE_SUBDIR = "subagents";
|
|
455
455
|
const CHECKS_FEATURE_SUBDIR = "checks";
|
|
456
456
|
const SKILLS_FEATURE_SUBDIR = "skills";
|
|
457
|
-
const CURATED_SKILLS_FEATURE_SUBDIR = join$
|
|
458
|
-
const RULESYNC_RULES_RELATIVE_DIR_PATH = join$
|
|
459
|
-
const RULESYNC_CURATED_RULES_RELATIVE_DIR_PATH = join$
|
|
460
|
-
const RULESYNC_COMMANDS_RELATIVE_DIR_PATH = join$
|
|
461
|
-
const RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH = join$
|
|
462
|
-
const RULESYNC_CHECKS_RELATIVE_DIR_PATH = join$
|
|
463
|
-
const RULESYNC_MCP_RELATIVE_FILE_PATH = join$
|
|
464
|
-
const RULESYNC_HOOKS_RELATIVE_FILE_PATH = join$
|
|
465
|
-
const RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH = join$
|
|
466
|
-
join$
|
|
467
|
-
const RULESYNC_HOOKS_LEGACY_RELATIVE_FILE_PATH = join$
|
|
468
|
-
const RULESYNC_PERMISSIONS_LEGACY_RELATIVE_FILE_PATH = join$
|
|
457
|
+
const CURATED_SKILLS_FEATURE_SUBDIR = join$310(SKILLS_FEATURE_SUBDIR, ".curated");
|
|
458
|
+
const RULESYNC_RULES_RELATIVE_DIR_PATH = join$310(RULESYNC_RELATIVE_DIR_PATH, RULES_FEATURE_SUBDIR);
|
|
459
|
+
const RULESYNC_CURATED_RULES_RELATIVE_DIR_PATH = join$310(RULESYNC_RELATIVE_DIR_PATH, CURATED_RULES_FEATURE_SUBDIR);
|
|
460
|
+
const RULESYNC_COMMANDS_RELATIVE_DIR_PATH = join$310(RULESYNC_RELATIVE_DIR_PATH, COMMANDS_FEATURE_SUBDIR);
|
|
461
|
+
const RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH = join$310(RULESYNC_RELATIVE_DIR_PATH, SUBAGENTS_FEATURE_SUBDIR);
|
|
462
|
+
const RULESYNC_CHECKS_RELATIVE_DIR_PATH = join$310(RULESYNC_RELATIVE_DIR_PATH, CHECKS_FEATURE_SUBDIR);
|
|
463
|
+
const RULESYNC_MCP_RELATIVE_FILE_PATH = join$310(RULESYNC_RELATIVE_DIR_PATH, "mcp.jsonc");
|
|
464
|
+
const RULESYNC_HOOKS_RELATIVE_FILE_PATH = join$310(RULESYNC_RELATIVE_DIR_PATH, "hooks.jsonc");
|
|
465
|
+
const RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH = join$310(RULESYNC_RELATIVE_DIR_PATH, "permissions.jsonc");
|
|
466
|
+
join$310(RULESYNC_RELATIVE_DIR_PATH, "mcp.json");
|
|
467
|
+
const RULESYNC_HOOKS_LEGACY_RELATIVE_FILE_PATH = join$310(RULESYNC_RELATIVE_DIR_PATH, "hooks.json");
|
|
468
|
+
const RULESYNC_PERMISSIONS_LEGACY_RELATIVE_FILE_PATH = join$310(RULESYNC_RELATIVE_DIR_PATH, "permissions.json");
|
|
469
469
|
const RULESYNC_AIIGNORE_FILE_NAME = ".aiignore";
|
|
470
|
-
const RULESYNC_AIIGNORE_RELATIVE_FILE_PATH = join$
|
|
470
|
+
const RULESYNC_AIIGNORE_RELATIVE_FILE_PATH = join$310(RULESYNC_RELATIVE_DIR_PATH, ".aiignore");
|
|
471
471
|
const RULESYNC_IGNORE_RELATIVE_FILE_PATH = ".rulesyncignore";
|
|
472
472
|
const RULESYNC_OVERVIEW_FILE_NAME = "overview.md";
|
|
473
|
-
const RULESYNC_SKILLS_RELATIVE_DIR_PATH = join$
|
|
474
|
-
const RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH = join$
|
|
473
|
+
const RULESYNC_SKILLS_RELATIVE_DIR_PATH = join$310(RULESYNC_RELATIVE_DIR_PATH, SKILLS_FEATURE_SUBDIR);
|
|
474
|
+
const RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH = join$310(RULESYNC_RELATIVE_DIR_PATH, CURATED_SKILLS_FEATURE_SUBDIR);
|
|
475
475
|
const RULESYNC_SOURCES_LOCK_RELATIVE_FILE_PATH = "rulesync.lock";
|
|
476
476
|
const RULESYNC_NPM_SOURCES_LOCK_RELATIVE_FILE_PATH = "rulesync-npm.lock.json";
|
|
477
477
|
const RULESYNC_MCP_FILE_NAME = "mcp.jsonc";
|
|
@@ -530,7 +530,8 @@ const rulesProcessorToolTargetTuple = [
|
|
|
530
530
|
"zcode",
|
|
531
531
|
"zed",
|
|
532
532
|
"zoocode",
|
|
533
|
-
"pool"
|
|
533
|
+
"pool",
|
|
534
|
+
"dsh"
|
|
534
535
|
];
|
|
535
536
|
const ignoreProcessorToolTargetTuple = [
|
|
536
537
|
"aiassistant",
|
|
@@ -708,7 +709,8 @@ const skillsProcessorToolTargetTuple = [
|
|
|
708
709
|
"devin",
|
|
709
710
|
"zcode",
|
|
710
711
|
"zed",
|
|
711
|
-
"zoocode"
|
|
712
|
+
"zoocode",
|
|
713
|
+
"dsh"
|
|
712
714
|
];
|
|
713
715
|
const hooksProcessorToolTargetTuple = [
|
|
714
716
|
"amp",
|
|
@@ -4236,10 +4238,12 @@ const PI_HOOK_EVENTS = [
|
|
|
4236
4238
|
"sessionEnd",
|
|
4237
4239
|
"preToolUse",
|
|
4238
4240
|
"postToolUse",
|
|
4241
|
+
"postToolUseFailure",
|
|
4239
4242
|
"preModelInvocation",
|
|
4240
4243
|
"postModelInvocation",
|
|
4241
4244
|
"beforeSubmitPrompt",
|
|
4242
4245
|
"stop",
|
|
4246
|
+
"notification",
|
|
4243
4247
|
"preCompact",
|
|
4244
4248
|
"postCompact"
|
|
4245
4249
|
];
|
|
@@ -4418,6 +4422,7 @@ const CODEXCLI_HOOK_EVENTS = [
|
|
|
4418
4422
|
"postToolUse",
|
|
4419
4423
|
"beforeSubmitPrompt",
|
|
4420
4424
|
"stop",
|
|
4425
|
+
"stopCancelled",
|
|
4421
4426
|
"permissionRequest",
|
|
4422
4427
|
"subagentStart",
|
|
4423
4428
|
"subagentStop",
|
|
@@ -4664,9 +4669,14 @@ const GROKCLI_HOOK_EVENTS = [
|
|
|
4664
4669
|
*
|
|
4665
4670
|
* Kimi Code also exposes `PermissionResult`, `Interrupt`, and the four events
|
|
4666
4671
|
* added in 0.32.0 (`TurnStarted`, `UserPromptQueued`, `TaskStarted`,
|
|
4667
|
-
* `SessionHeartbeat`), none of which
|
|
4668
|
-
*
|
|
4669
|
-
*
|
|
4672
|
+
* `SessionHeartbeat`), none of which is mapped onto a canonical rulesync event
|
|
4673
|
+
* here. `Interrupt` (fires instead of `Stop` when the user interrupts a turn)
|
|
4674
|
+
* does have a canonical shape — `stopCancelled`, which Grok CLI and Codex CLI
|
|
4675
|
+
* map — but Kimi keeps it native-only for now, since an existing `kimi-code`
|
|
4676
|
+
* override addressing it by name would otherwise double up with a canonical
|
|
4677
|
+
* `stopCancelled` block; folding it in is a follow-up. All six are listed in
|
|
4678
|
+
* `KIMI_CODE_NATIVE_HOOK_EVENTS` so a per-tool `kimi-code` override can
|
|
4679
|
+
* address them by their native name.
|
|
4670
4680
|
*
|
|
4671
4681
|
* @see https://moonshotai.github.io/kimi-code/en/customization/hooks.html
|
|
4672
4682
|
*/
|
|
@@ -5105,7 +5115,7 @@ const CANONICAL_TO_KILO_EVENT_NAMES = CANONICAL_TO_OPENCODE_EVENT_NAMES;
|
|
|
5105
5115
|
* Stop, this also fires before Pi auto-retries or auto-compacts —
|
|
5106
5116
|
* `agent_settled` would skip queued follow-ups instead, a pure trade-off).
|
|
5107
5117
|
* Pi events without a faithful canonical counterpart (e.g. `turn_start`,
|
|
5108
|
-
* `agent_settled`) are intentionally unmapped.
|
|
5118
|
+
* `agent_settled`, `ui_prompt_end`) are intentionally unmapped.
|
|
5109
5119
|
*
|
|
5110
5120
|
* @see https://github.com/earendil-works/pi/blob/main/packages/coding-agent/docs/extensions.md
|
|
5111
5121
|
*/
|
|
@@ -5114,10 +5124,12 @@ const CANONICAL_TO_PI_EVENT_NAMES = {
|
|
|
5114
5124
|
sessionEnd: "session_shutdown",
|
|
5115
5125
|
preToolUse: "tool_call",
|
|
5116
5126
|
postToolUse: "tool_result",
|
|
5127
|
+
postToolUseFailure: "tool_result",
|
|
5117
5128
|
preModelInvocation: "context",
|
|
5118
5129
|
postModelInvocation: "message_end",
|
|
5119
5130
|
beforeSubmitPrompt: "input",
|
|
5120
5131
|
stop: "agent_end",
|
|
5132
|
+
notification: "ui_prompt_start",
|
|
5121
5133
|
preCompact: "session_before_compact",
|
|
5122
5134
|
postCompact: "session_compact"
|
|
5123
5135
|
};
|
|
@@ -5199,6 +5211,7 @@ const CANONICAL_TO_CODEXCLI_EVENT_NAMES = {
|
|
|
5199
5211
|
postToolUse: "PostToolUse",
|
|
5200
5212
|
beforeSubmitPrompt: "UserPromptSubmit",
|
|
5201
5213
|
stop: "Stop",
|
|
5214
|
+
stopCancelled: "Interrupt",
|
|
5202
5215
|
permissionRequest: "PermissionRequest",
|
|
5203
5216
|
subagentStart: "SubagentStart",
|
|
5204
5217
|
subagentStop: "SubagentStop",
|
|
@@ -8000,7 +8013,7 @@ const RulesyncRuleFrontmatterSchema = zod_mini.z.object({
|
|
|
8000
8013
|
extends: zod_mini.z.optional(zod_mini.z.string()),
|
|
8001
8014
|
facet: zod_mini.z.optional(zod_mini.z.enum(["policies", "output-contracts"]))
|
|
8002
8015
|
})),
|
|
8003
|
-
factorydroid: zod_mini.z.optional(zod_mini.z.looseObject({ channel: zod_mini.z.optional(zod_mini.z.enum(["design"])) }))
|
|
8016
|
+
factorydroid: zod_mini.z.optional(zod_mini.z.looseObject({ channel: zod_mini.z.optional(zod_mini.z.enum(["design", "threat-model"])) }))
|
|
8004
8017
|
});
|
|
8005
8018
|
/**
|
|
8006
8019
|
* The `agentsmd.subprojectPath` every consumer should act on, resolved once so
|
|
@@ -9214,6 +9227,12 @@ const RulesyncSkillFrontmatterSchema = zod_mini.z.looseObject({
|
|
|
9214
9227
|
"disable-model-invocation": zod_mini.z.optional(zod_mini.z.boolean()),
|
|
9215
9228
|
"user-invocable": zod_mini.z.optional(zod_mini.z.boolean())
|
|
9216
9229
|
})),
|
|
9230
|
+
dsh: zod_mini.z.optional(zod_mini.z.looseObject({
|
|
9231
|
+
whenToUse: zod_mini.z.optional(zod_mini.z.string()),
|
|
9232
|
+
metadata: zod_mini.z.optional(zod_mini.z.looseObject({})),
|
|
9233
|
+
"disable-model-invocation": zod_mini.z.optional(zod_mini.z.boolean()),
|
|
9234
|
+
"user-invocable": zod_mini.z.optional(zod_mini.z.boolean())
|
|
9235
|
+
})),
|
|
9217
9236
|
"kimi-code": zod_mini.z.optional(zod_mini.z.looseObject({
|
|
9218
9237
|
type: zod_mini.z.optional(zod_mini.z.enum([
|
|
9219
9238
|
"prompt",
|
|
@@ -9478,7 +9497,7 @@ async function getLocalSkillDirNames(sourceTree) {
|
|
|
9478
9497
|
*
|
|
9479
9498
|
* The rulesync skill frontmatter exposes a root-level `disable-model-invocation`
|
|
9480
9499
|
* default that applies to every tool supporting the flag (claudecode, copilot,
|
|
9481
|
-
* copilotcli, crush, cursor, zed, pi, qwencode, grokcli, factorydroid). Each tool's own section may override that
|
|
9500
|
+
* copilotcli, crush, cursor, zed, pi, qwencode, grokcli, factorydroid, dsh). Each tool's own section may override that
|
|
9482
9501
|
* default with a per-target value. A defined section value (including `false`)
|
|
9483
9502
|
* always wins over the root default.
|
|
9484
9503
|
*
|
|
@@ -9497,7 +9516,7 @@ function resolveDisableModelInvocation({ rootFrontmatter, section }) {
|
|
|
9497
9516
|
*
|
|
9498
9517
|
* The rulesync skill frontmatter exposes a root-level `user-invocable` default
|
|
9499
9518
|
* that applies to every tool supporting the flag (claudecode, copilot,
|
|
9500
|
-
* copilotcli, crush, cursor, qwencode, vibe, grokcli, factorydroid). Each tool's own section may override that default with a
|
|
9519
|
+
* copilotcli, crush, cursor, qwencode, vibe, grokcli, factorydroid, dsh). Each tool's own section may override that default with a
|
|
9501
9520
|
* per-target value. A defined section value (including `false`) always wins
|
|
9502
9521
|
* over the root default.
|
|
9503
9522
|
*
|
|
@@ -11099,6 +11118,13 @@ const FACTORYDROID_RULE_FILE_NAME = "AGENTS.md";
|
|
|
11099
11118
|
* @see https://docs.factory.ai/cli/configuration/agents-md
|
|
11100
11119
|
*/
|
|
11101
11120
|
const FACTORYDROID_DESIGN_FILE_NAME = "DESIGN.md";
|
|
11121
|
+
/**
|
|
11122
|
+
* Factory's Security Review threat model, kept inside the tool directory:
|
|
11123
|
+
* "if `.factory/threat-model.md` exists, Droid uses it as the attack-surface
|
|
11124
|
+
* map". Documented only as a repository file, so project scope only.
|
|
11125
|
+
* @see https://docs.factory.ai/software-factory/security-review
|
|
11126
|
+
*/
|
|
11127
|
+
const FACTORYDROID_THREAT_MODEL_FILE_NAME = "threat-model.md";
|
|
11102
11128
|
const FACTORYDROID_MCP_FILE_NAME = "mcp.json";
|
|
11103
11129
|
const FACTORYDROID_SETTINGS_FILE_NAME = "settings.json";
|
|
11104
11130
|
const FACTORYDROID_HOOKS_FILE_NAME = "hooks.json";
|
|
@@ -11740,7 +11766,10 @@ function parseSharedConfig({ format, fileContent, filePath, invalidRootPolicy =
|
|
|
11740
11766
|
throw new Error(`Failed to parse shared config${at}: ${formatError(error)}`, { cause: error });
|
|
11741
11767
|
}
|
|
11742
11768
|
if (!isPlainObject$1(sanitized)) {
|
|
11743
|
-
if (invalidRootPolicy === "error")
|
|
11769
|
+
if (invalidRootPolicy === "error") {
|
|
11770
|
+
const reason = /* @__PURE__ */ new Error("expected a mapping at the root");
|
|
11771
|
+
throw new Error(`Failed to parse shared config${at}: ${reason.message}`, { cause: reason });
|
|
11772
|
+
}
|
|
11744
11773
|
return {};
|
|
11745
11774
|
}
|
|
11746
11775
|
return sanitized;
|
|
@@ -12742,7 +12771,9 @@ const SHARED_CONFIG_OWNERSHIP = {
|
|
|
12742
12771
|
}
|
|
12743
12772
|
},
|
|
12744
12773
|
".augment/settings.json": {
|
|
12745
|
-
format: "
|
|
12774
|
+
format: "jsonc",
|
|
12775
|
+
invalidRootPolicy: "error",
|
|
12776
|
+
jsoncParseErrors: "error",
|
|
12746
12777
|
features: {
|
|
12747
12778
|
mcp: {
|
|
12748
12779
|
kind: "replace-owned-keys",
|
|
@@ -13336,7 +13367,9 @@ function slugForGate({ gate, index, scope }) {
|
|
|
13336
13367
|
* agent step prompt as a completion directive;
|
|
13337
13368
|
* - with `command` in the check's `takt` frontmatter block, a **command
|
|
13338
13369
|
* gate** (`{type: command, name, command, cwd, timeout_ms}`), which Takt
|
|
13339
|
-
* runs after the step
|
|
13370
|
+
* runs after the step (since 0.62.0: after rule resolution and before the
|
|
13371
|
+
* transition, unless the selected rule sets `command_gates: skip`) and
|
|
13372
|
+
* fails on a non-zero exit code.
|
|
13340
13373
|
*
|
|
13341
13374
|
* `steps` / `personas` in that block scope a gate to named workflow steps or
|
|
13342
13375
|
* personas (`workflow_overrides.steps.<step>.quality_gates`); an unscoped gate
|
|
@@ -20598,14 +20631,14 @@ var AntigravityPluginHooks = class extends AntigravityIdeHooks {
|
|
|
20598
20631
|
* Returns `null` when neither file exists and no `baseFallbackContent` is
|
|
20599
20632
|
* given, so callers can tell "no settings at this scope" apart from empty ones.
|
|
20600
20633
|
*/
|
|
20601
|
-
async function readSettingsWithLocalOverlay({ outputRoot, relativeDirPath, baseFileName, localFileName, toolLabel, baseFallbackContent, sensitiveKeys = [], quiet = false, merge, logger }) {
|
|
20634
|
+
async function readSettingsWithLocalOverlay({ outputRoot, relativeDirPath, baseFileName, localFileName, toolLabel, baseFallbackContent, sensitiveKeys = [], quiet = false, parse = JSON.parse, merge, logger }) {
|
|
20602
20635
|
const baseContent = await readFileContentOrNull((0, node_path.join)(outputRoot, relativeDirPath, baseFileName)) ?? baseFallbackContent ?? null;
|
|
20603
20636
|
const localContent = await readFileContentOrNull((0, node_path.join)(outputRoot, relativeDirPath, localFileName));
|
|
20604
20637
|
if (localContent === null) return baseContent;
|
|
20605
20638
|
const configPath = (0, node_path.join)(relativeDirPath, localFileName);
|
|
20606
20639
|
let localParsed;
|
|
20607
20640
|
try {
|
|
20608
|
-
localParsed =
|
|
20641
|
+
localParsed = parse(localContent);
|
|
20609
20642
|
} catch (error) {
|
|
20610
20643
|
throw new Error(`Failed to parse ${toolLabel} settings at ${configPath}: ${formatError(error)}`, { cause: error });
|
|
20611
20644
|
}
|
|
@@ -20614,7 +20647,7 @@ async function readSettingsWithLocalOverlay({ outputRoot, relativeDirPath, baseF
|
|
|
20614
20647
|
if (baseContent !== null) {
|
|
20615
20648
|
let parsed;
|
|
20616
20649
|
try {
|
|
20617
|
-
parsed =
|
|
20650
|
+
parsed = parse(baseContent);
|
|
20618
20651
|
} catch {
|
|
20619
20652
|
return baseContent;
|
|
20620
20653
|
}
|
|
@@ -20673,6 +20706,51 @@ function warnAboutLocalKeys({ localParsed, configPath, toolLabel, sensitiveKeys,
|
|
|
20673
20706
|
//#endregion
|
|
20674
20707
|
//#region src/utils/augmentcode-settings.ts
|
|
20675
20708
|
/**
|
|
20709
|
+
* The one place that spells out how an AugmentCode settings file is parsed.
|
|
20710
|
+
*
|
|
20711
|
+
* Auggie reads `settings.json` / `settings.local.json` as JSON with Comments —
|
|
20712
|
+
* "The files support JSON with Comments (JSONC), allowing comments and trailing
|
|
20713
|
+
* commas for better documentation." (https://docs.augmentcode.com/cli/config) —
|
|
20714
|
+
* so a bare `JSON.parse` rejects a hand-written file the CLI itself accepts. The
|
|
20715
|
+
* parse is fail-closed: a syntax error or a non-object root throws instead of
|
|
20716
|
+
* yielding a partial document, because every caller either merges its own keys
|
|
20717
|
+
* back into this file or imports permissions from it, and neither may proceed
|
|
20718
|
+
* on a file it could not read in full. An empty file parses as `{}`.
|
|
20719
|
+
*
|
|
20720
|
+
* The generate direction registers the same file as `jsonc` in
|
|
20721
|
+
* `SHARED_CONFIG_OWNERSHIP`, so the in-place patch reads it the same way.
|
|
20722
|
+
*
|
|
20723
|
+
* Throws the bare reason (the parser's own error), without naming the file;
|
|
20724
|
+
* `parseAugmentcodeSettingsDocument` adds that, and so does
|
|
20725
|
+
* `readSettingsWithLocalOverlay` when this is handed over as its `parse`.
|
|
20726
|
+
*/
|
|
20727
|
+
function parseAugmentcodeSettingsContent(fileContent) {
|
|
20728
|
+
try {
|
|
20729
|
+
return parseSharedConfig({
|
|
20730
|
+
format: "jsonc",
|
|
20731
|
+
fileContent,
|
|
20732
|
+
invalidRootPolicy: "error",
|
|
20733
|
+
jsoncParseErrors: "error"
|
|
20734
|
+
});
|
|
20735
|
+
} catch (error) {
|
|
20736
|
+
if (error instanceof Error && error.cause !== void 0) throw error.cause;
|
|
20737
|
+
throw error;
|
|
20738
|
+
}
|
|
20739
|
+
}
|
|
20740
|
+
/**
|
|
20741
|
+
* Parse an AugmentCode settings file (`settings.json` / `settings.local.json`)
|
|
20742
|
+
* into a plain object, naming `configPath` in the error when it cannot be read.
|
|
20743
|
+
* See `parseAugmentcodeSettingsContent` for the format and the fail-closed
|
|
20744
|
+
* policy.
|
|
20745
|
+
*/
|
|
20746
|
+
function parseAugmentcodeSettingsDocument({ fileContent, configPath }) {
|
|
20747
|
+
try {
|
|
20748
|
+
return parseAugmentcodeSettingsContent(fileContent);
|
|
20749
|
+
} catch (error) {
|
|
20750
|
+
throw new Error(`Failed to parse AugmentCode settings at ${configPath}: ${formatError(error)}`, { cause: error });
|
|
20751
|
+
}
|
|
20752
|
+
}
|
|
20753
|
+
/**
|
|
20676
20754
|
* Top-level keys AugmentCode *replaces* (higher-precedence wins wholesale)
|
|
20677
20755
|
* rather than combining across tiers. Everything else combines.
|
|
20678
20756
|
*
|
|
@@ -20747,6 +20825,7 @@ async function readAugmentcodeSettingsWithLocalOverlay({ outputRoot, relativeDir
|
|
|
20747
20825
|
localFileName: "settings.local.json",
|
|
20748
20826
|
toolLabel: "AugmentCode",
|
|
20749
20827
|
sensitiveKeys: AUGMENTCODE_GUARDRAIL_KEYS,
|
|
20828
|
+
parse: parseAugmentcodeSettingsContent,
|
|
20750
20829
|
baseFallbackContent,
|
|
20751
20830
|
merge: combineAugmentSettings,
|
|
20752
20831
|
logger
|
|
@@ -20826,8 +20905,10 @@ var AugmentcodeHooks = class AugmentcodeHooks extends ToolHooks {
|
|
|
20826
20905
|
const existingContent = await readFileContentOrNull(filePath) ?? JSON.stringify({}, null, 2);
|
|
20827
20906
|
let existingHooks = {};
|
|
20828
20907
|
try {
|
|
20829
|
-
const
|
|
20830
|
-
|
|
20908
|
+
const candidate = parseAugmentcodeSettingsDocument({
|
|
20909
|
+
fileContent: existingContent,
|
|
20910
|
+
configPath: (0, node_path.join)(paths.relativeDirPath, paths.relativeFilePath)
|
|
20911
|
+
}).hooks;
|
|
20831
20912
|
if (candidate && typeof candidate === "object" && !Array.isArray(candidate)) existingHooks = candidate;
|
|
20832
20913
|
} catch {
|
|
20833
20914
|
existingHooks = {};
|
|
@@ -20849,7 +20930,8 @@ var AugmentcodeHooks = class AugmentcodeHooks extends ToolHooks {
|
|
|
20849
20930
|
...preservedHooks,
|
|
20850
20931
|
...augmentHooks
|
|
20851
20932
|
} },
|
|
20852
|
-
filePath
|
|
20933
|
+
filePath,
|
|
20934
|
+
logger
|
|
20853
20935
|
});
|
|
20854
20936
|
return new AugmentcodeHooks({
|
|
20855
20937
|
outputRoot,
|
|
@@ -20862,7 +20944,10 @@ var AugmentcodeHooks = class AugmentcodeHooks extends ToolHooks {
|
|
|
20862
20944
|
toRulesyncHooks({ logger } = {}) {
|
|
20863
20945
|
let settings;
|
|
20864
20946
|
try {
|
|
20865
|
-
settings =
|
|
20947
|
+
settings = parseAugmentcodeSettingsDocument({
|
|
20948
|
+
fileContent: this.getFileContent(),
|
|
20949
|
+
configPath: (0, node_path.join)(this.getRelativeDirPath(), this.getRelativeFilePath())
|
|
20950
|
+
});
|
|
20866
20951
|
} catch (error) {
|
|
20867
20952
|
throw new Error(`Failed to parse AugmentCode hooks content in ${(0, node_path.join)(this.getRelativeDirPath(), this.getRelativeFilePath())}: ${formatError(error)}`, { cause: error });
|
|
20868
20953
|
}
|
|
@@ -24177,9 +24262,10 @@ function stripTrustedDirectoryWrapper(command) {
|
|
|
24177
24262
|
* to filter on in the first place.
|
|
24178
24263
|
*
|
|
24179
24264
|
* Keyed on native names because the check runs after the canonical → native
|
|
24180
|
-
* mapping: `SessionHeartbeat` and `Interrupt`
|
|
24181
|
-
*
|
|
24182
|
-
*
|
|
24265
|
+
* mapping: `SessionHeartbeat` and `Interrupt` are not mapped from a canonical
|
|
24266
|
+
* event for Kimi (`Interrupt` is the shape of canonical `stopCancelled`, which
|
|
24267
|
+
* other tools map, but stays native-only here) and are only reachable through
|
|
24268
|
+
* a per-tool `kimi-code` override naming them directly.
|
|
24183
24269
|
*
|
|
24184
24270
|
* Deliberately narrower than Claude Code's equivalent set: Kimi Code's
|
|
24185
24271
|
* `UserPromptSubmit` matches the submitted prompt text, and `PermissionResult`
|
|
@@ -24910,6 +24996,15 @@ const PI_TOOL_EVENTS = /* @__PURE__ */ new Set(["tool_call", "tool_result"]);
|
|
|
24910
24996
|
*/
|
|
24911
24997
|
const PI_ASSISTANT_MESSAGE_EVENTS = /* @__PURE__ */ new Set(["message_end"]);
|
|
24912
24998
|
/**
|
|
24999
|
+
* Canonical events whose Pi event also fires when nothing went wrong. Pi's
|
|
25000
|
+
* `tool_result` fires for every finished tool call and exposes `event.isError`,
|
|
25001
|
+
* so a `postToolUseFailure` handler is gated on that flag while a `postToolUse`
|
|
25002
|
+
* handler on the same Pi event runs regardless.
|
|
25003
|
+
*
|
|
25004
|
+
* @see https://github.com/earendil-works/pi/blob/v0.85.1/packages/coding-agent/docs/extensions.md#tool_result
|
|
25005
|
+
*/
|
|
25006
|
+
const PI_ERROR_GATED_CANONICAL_EVENTS = /* @__PURE__ */ new Set(["postToolUseFailure"]);
|
|
25007
|
+
/**
|
|
24913
25008
|
* `tool_call` is Pi's tool gate. Its return contract is
|
|
24914
25009
|
* `{ block: true, reason?: string, terminate?: boolean }`.
|
|
24915
25010
|
*
|
|
@@ -25055,7 +25150,8 @@ function collectPiHandlers({ effectiveHooks, eventMap }) {
|
|
|
25055
25150
|
if (!def.command) continue;
|
|
25056
25151
|
handlers.push({
|
|
25057
25152
|
command: def.command,
|
|
25058
|
-
matcher: def.matcher ? def.matcher : void 0
|
|
25153
|
+
matcher: def.matcher ? def.matcher : void 0,
|
|
25154
|
+
onlyOnError: PI_ERROR_GATED_CANONICAL_EVENTS.has(canonicalEvent)
|
|
25059
25155
|
});
|
|
25060
25156
|
}
|
|
25061
25157
|
if (handlers.length > 0) {
|
|
@@ -25068,10 +25164,13 @@ function collectPiHandlers({ effectiveHooks, eventMap }) {
|
|
|
25068
25164
|
}
|
|
25069
25165
|
function buildCommandLines({ handler, usesToolName, blocking }) {
|
|
25070
25166
|
const lines = [];
|
|
25071
|
-
const
|
|
25167
|
+
const conditions = [];
|
|
25168
|
+
if (handler.onlyOnError) conditions.push("event.isError");
|
|
25169
|
+
if (usesToolName && handler.matcher) conditions.push(`new RegExp(${matcherToEmbeddedLiteral(handler.matcher)}).test(event.toolName)`);
|
|
25170
|
+
const gated = conditions.length > 0;
|
|
25072
25171
|
const indent = gated ? " " : " ";
|
|
25073
25172
|
const embeddedCommand = JSON.stringify(handler.command);
|
|
25074
|
-
if (gated
|
|
25173
|
+
if (gated) lines.push(` if (${conditions.join(" && ")}) {`);
|
|
25075
25174
|
const onFailure = FAILURE_LINES_BY_MODE[blocking];
|
|
25076
25175
|
if (onFailure.length > 0) {
|
|
25077
25176
|
lines.push(`${indent}try {`);
|
|
@@ -25089,8 +25188,9 @@ function buildSubscriptionLines(handlerGroups) {
|
|
|
25089
25188
|
const blocking = PI_BLOCKING_MODE_BY_EVENT[piEvent] ?? "none";
|
|
25090
25189
|
const isPromptGate = blocking === "prompt";
|
|
25091
25190
|
const usesToolName = PI_TOOL_EVENTS.has(piEvent) && handlers.some((h) => h.matcher);
|
|
25191
|
+
const usesErrorFlag = handlers.some((h) => h.onlyOnError);
|
|
25092
25192
|
const gatesOnAssistant = PI_ASSISTANT_MESSAGE_EVENTS.has(piEvent);
|
|
25093
|
-
const params = isPromptGate ? "event, ctx" : usesToolName || gatesOnAssistant || isPromptGate ? "event" : "";
|
|
25193
|
+
const params = isPromptGate ? "event, ctx" : usesToolName || usesErrorFlag || gatesOnAssistant || isPromptGate ? "event" : "";
|
|
25094
25194
|
lines.push(` pi.on(${JSON.stringify(piEvent)}, async (${params}) => {`);
|
|
25095
25195
|
if (gatesOnAssistant) lines.push(` if (event.message.role !== "assistant") return;`);
|
|
25096
25196
|
if (isPromptGate) lines.push(` if (event.source === "extension") return { action: "continue" };`);
|
|
@@ -25112,6 +25212,8 @@ function buildSubscriptionLines(handlerGroups) {
|
|
|
25112
25212
|
* gate — where a hook command that exits non-zero denies the call with
|
|
25113
25213
|
* `{ block: true, reason }`, and on `input` — Pi's prompt-submission gate —
|
|
25114
25214
|
* where a non-zero exit cancels the prompt with `{ action: "handled" }`.
|
|
25215
|
+
* `postToolUse` and `postToolUseFailure` share Pi's `tool_result` event; the
|
|
25216
|
+
* latter's commands run only when `event.isError` is set.
|
|
25115
25217
|
*
|
|
25116
25218
|
* @see https://github.com/earendil-works/pi/blob/main/packages/coding-agent/docs/extensions.md
|
|
25117
25219
|
*/
|
|
@@ -25680,12 +25782,29 @@ const VIBE_HOOKS_FILE_NAME = "hooks.toml";
|
|
|
25680
25782
|
const VIBE_TOOL_EVENTS = /* @__PURE__ */ new Set(["pre_tool", "post_tool"]);
|
|
25681
25783
|
const SUPPORTED_VIBE_EVENTS = new Set(VIBE_HOOK_EVENTS);
|
|
25682
25784
|
/**
|
|
25785
|
+
* Vibe drops a hook whose `command` contains a backslash when it loads
|
|
25786
|
+
* `hooks.toml` (since v2.25.1; `load_hooks_config` at v2.25.3: "Hook '<name>' skipped:
|
|
25787
|
+
* backslash paths are not supported in hook commands. Use forward slashes
|
|
25788
|
+
* instead."), because its exec-based executor tokenizes the command with
|
|
25789
|
+
* `shlex.split`, which eats the backslash. The hook is still written — the
|
|
25790
|
+
* command is the user's to fix — but it is worth a warning, since Vibe itself
|
|
25791
|
+
* only reports the skip as a config issue rather than failing the run.
|
|
25792
|
+
* @see https://github.com/mistralai/mistral-vibe/blob/v2.25.3/vibe/core/hooks/config.py
|
|
25793
|
+
*/
|
|
25794
|
+
function warnAboutBackslashCommand({ name, command, logger }) {
|
|
25795
|
+
if (!command.includes("\\")) return;
|
|
25796
|
+
logger?.warn(`the command of hook ${quoteValueForWarning(name)} contains a backslash, which Vibe rejects when it loads hooks.toml — the hook is skipped with "backslash paths are not supported in hook commands". Use forward slashes and avoid backslash escapes so the hook runs.`);
|
|
25797
|
+
}
|
|
25798
|
+
/**
|
|
25683
25799
|
* Build the flat `[[hooks]]` array for `.vibe/hooks.toml` from a canonical
|
|
25684
25800
|
* hooks config. Vibe uses a flat array where each entry carries its own event
|
|
25685
25801
|
* `type`, tool-name `match` glob/regex, and `command`. Only `type: "command"`
|
|
25686
|
-
* canonical hooks are emitted
|
|
25802
|
+
* canonical hooks are emitted: a Vibe hook is a command line, which the legacy
|
|
25803
|
+
* backend hands to a shell and the unified harness tokenizes with `shlex.split`
|
|
25804
|
+
* and executes directly (v2.25.1: "Hook commands run without a shell to
|
|
25805
|
+
* prevent injection via hooks.toml").
|
|
25687
25806
|
*/
|
|
25688
|
-
function canonicalToVibeHooks(config, toolOverride) {
|
|
25807
|
+
function canonicalToVibeHooks({ config, toolOverride, logger }) {
|
|
25689
25808
|
const shared = {};
|
|
25690
25809
|
for (const [event, defs] of Object.entries(config.hooks)) if (SUPPORTED_VIBE_EVENTS.has(event)) shared[event] = defs;
|
|
25691
25810
|
const effective = {
|
|
@@ -25704,6 +25823,11 @@ function canonicalToVibeHooks(config, toolOverride) {
|
|
|
25704
25823
|
if ((def.type ?? "command") !== "command") continue;
|
|
25705
25824
|
if (typeof def.command !== "string") continue;
|
|
25706
25825
|
const name = typeof def.name === "string" ? def.name : `${vibeEvent}-${index}`;
|
|
25826
|
+
warnAboutBackslashCommand({
|
|
25827
|
+
name,
|
|
25828
|
+
command: def.command,
|
|
25829
|
+
logger
|
|
25830
|
+
});
|
|
25707
25831
|
const isToolEvent = VIBE_TOOL_EVENTS.has(vibeEvent);
|
|
25708
25832
|
const entry = {
|
|
25709
25833
|
name,
|
|
@@ -25804,10 +25928,14 @@ var VibeHooks = class VibeHooks extends ToolHooks {
|
|
|
25804
25928
|
validate
|
|
25805
25929
|
});
|
|
25806
25930
|
}
|
|
25807
|
-
static async fromRulesyncHooks({ outputRoot = process.cwd(), rulesyncHooks, validate = true, global = false }) {
|
|
25931
|
+
static async fromRulesyncHooks({ outputRoot = process.cwd(), rulesyncHooks, validate = true, global = false, logger }) {
|
|
25808
25932
|
const paths = VibeHooks.getSettablePaths({ global });
|
|
25809
25933
|
const config = rulesyncHooks.getJson();
|
|
25810
|
-
const vibeHooks = canonicalToVibeHooks(
|
|
25934
|
+
const vibeHooks = canonicalToVibeHooks({
|
|
25935
|
+
config,
|
|
25936
|
+
toolOverride: config.vibe?.hooks,
|
|
25937
|
+
logger
|
|
25938
|
+
});
|
|
25811
25939
|
const fileContent = smol_toml.stringify(vibeHooks);
|
|
25812
25940
|
return new VibeHooks({
|
|
25813
25941
|
outputRoot,
|
|
@@ -28740,15 +28868,10 @@ var AntigravityPluginMcp = class extends AntigravityIdeMcp {
|
|
|
28740
28868
|
//#endregion
|
|
28741
28869
|
//#region src/features/mcp/augmentcode-mcp.ts
|
|
28742
28870
|
function parseAugmentcodeSettings(fileContent, relativeDirPath, relativeFilePath) {
|
|
28743
|
-
|
|
28744
|
-
|
|
28745
|
-
|
|
28746
|
-
|
|
28747
|
-
} catch (error) {
|
|
28748
|
-
throw new Error(`Failed to parse AugmentCode settings at ${configPath}: ${formatError(error)}`, { cause: error });
|
|
28749
|
-
}
|
|
28750
|
-
if (!isPlainObject$1(parsed)) throw new Error(`Failed to parse AugmentCode settings at ${configPath}: expected a JSON object`);
|
|
28751
|
-
return parsed;
|
|
28871
|
+
return parseAugmentcodeSettingsDocument({
|
|
28872
|
+
fileContent,
|
|
28873
|
+
configPath: (0, node_path.join)(relativeDirPath, relativeFilePath)
|
|
28874
|
+
});
|
|
28752
28875
|
}
|
|
28753
28876
|
/**
|
|
28754
28877
|
* AugmentCode (Auggie CLI) MCP servers.
|
|
@@ -28805,7 +28928,7 @@ var AugmentcodeMcp = class AugmentcodeMcp extends ToolMcp {
|
|
|
28805
28928
|
global
|
|
28806
28929
|
});
|
|
28807
28930
|
}
|
|
28808
|
-
static async fromRulesyncMcp({ outputRoot = process.cwd(), rulesyncMcp, validate = true, global = false }) {
|
|
28931
|
+
static async fromRulesyncMcp({ outputRoot = process.cwd(), rulesyncMcp, validate = true, global = false, logger }) {
|
|
28809
28932
|
const paths = this.getSettablePaths({ global });
|
|
28810
28933
|
const filePath = (0, node_path.join)(outputRoot, paths.relativeDirPath, paths.relativeFilePath);
|
|
28811
28934
|
const existingContent = await readFileContentOrNull(filePath) ?? JSON.stringify({}, null, 2);
|
|
@@ -28818,7 +28941,8 @@ var AugmentcodeMcp = class AugmentcodeMcp extends ToolMcp {
|
|
|
28818
28941
|
feature: "mcp",
|
|
28819
28942
|
existingContent,
|
|
28820
28943
|
patch: { mcpServers: rulesyncMcp.getMcpServers() },
|
|
28821
|
-
filePath
|
|
28944
|
+
filePath,
|
|
28945
|
+
logger
|
|
28822
28946
|
}),
|
|
28823
28947
|
validate,
|
|
28824
28948
|
global
|
|
@@ -36991,7 +37115,10 @@ var AugmentcodePermissions = class AugmentcodePermissions extends ToolPermission
|
|
|
36991
37115
|
const existingContent = await readFileContentOrNull(filePath) ?? "{}";
|
|
36992
37116
|
let settings;
|
|
36993
37117
|
try {
|
|
36994
|
-
const parsed =
|
|
37118
|
+
const parsed = parseAugmentcodeSettingsDocument({
|
|
37119
|
+
fileContent: existingContent,
|
|
37120
|
+
configPath: (0, node_path.join)(paths.relativeDirPath, paths.relativeFilePath)
|
|
37121
|
+
});
|
|
36995
37122
|
const result = AugmentSettingsSchema.safeParse(parsed);
|
|
36996
37123
|
if (!result.success) throw new Error(formatError(result.error));
|
|
36997
37124
|
settings = result.data;
|
|
@@ -37031,7 +37158,8 @@ var AugmentcodePermissions = class AugmentcodePermissions extends ToolPermission
|
|
|
37031
37158
|
feature: "permissions",
|
|
37032
37159
|
existingContent,
|
|
37033
37160
|
patch: { toolPermissions: [...specialEntries, ...sortedBasic] },
|
|
37034
|
-
filePath
|
|
37161
|
+
filePath,
|
|
37162
|
+
logger
|
|
37035
37163
|
});
|
|
37036
37164
|
return new AugmentcodePermissions({
|
|
37037
37165
|
outputRoot,
|
|
@@ -37044,7 +37172,10 @@ var AugmentcodePermissions = class AugmentcodePermissions extends ToolPermission
|
|
|
37044
37172
|
toRulesyncPermissions() {
|
|
37045
37173
|
let settings;
|
|
37046
37174
|
try {
|
|
37047
|
-
const parsed =
|
|
37175
|
+
const parsed = parseAugmentcodeSettingsDocument({
|
|
37176
|
+
fileContent: this.getFileContent(),
|
|
37177
|
+
configPath: (0, node_path.join)(this.getRelativeDirPath(), this.getRelativeFilePath())
|
|
37178
|
+
});
|
|
37048
37179
|
const result = AugmentSettingsSchema.safeParse(parsed);
|
|
37049
37180
|
if (!result.success) throw new Error(formatError(result.error));
|
|
37050
37181
|
settings = result.data;
|
|
@@ -37062,7 +37193,10 @@ var AugmentcodePermissions = class AugmentcodePermissions extends ToolPermission
|
|
|
37062
37193
|
}
|
|
37063
37194
|
validate() {
|
|
37064
37195
|
try {
|
|
37065
|
-
const parsed =
|
|
37196
|
+
const parsed = parseAugmentcodeSettingsDocument({
|
|
37197
|
+
fileContent: this.fileContent || "{}",
|
|
37198
|
+
configPath: (0, node_path.join)(this.getRelativeDirPath(), this.getRelativeFilePath())
|
|
37199
|
+
});
|
|
37066
37200
|
const result = AugmentSettingsSchema.safeParse(parsed);
|
|
37067
37201
|
if (!result.success) return {
|
|
37068
37202
|
success: false,
|
|
@@ -41412,19 +41546,27 @@ const CATEGORY_TO_GROK_TOOL = {
|
|
|
41412
41546
|
edit: "Edit",
|
|
41413
41547
|
write: "Edit",
|
|
41414
41548
|
grep: "Grep",
|
|
41549
|
+
glob: "Glob",
|
|
41415
41550
|
webfetch: "WebFetch",
|
|
41416
|
-
websearch: "WebSearch"
|
|
41551
|
+
websearch: "WebSearch",
|
|
41552
|
+
agent: "AgentMessage"
|
|
41417
41553
|
};
|
|
41418
41554
|
const GROK_TOOL_TO_CATEGORY = {
|
|
41419
41555
|
Bash: "bash",
|
|
41420
41556
|
Read: "read",
|
|
41421
41557
|
Edit: "edit",
|
|
41422
41558
|
Grep: "grep",
|
|
41559
|
+
Glob: "glob",
|
|
41423
41560
|
WebFetch: "webfetch",
|
|
41424
|
-
WebSearch: "websearch"
|
|
41561
|
+
WebSearch: "websearch",
|
|
41562
|
+
AgentMessage: "agent",
|
|
41563
|
+
...Object.fromEntries(["SendSubagentMessage", "SendAgentMessage"].map((tool) => [tool, "agent"]))
|
|
41425
41564
|
};
|
|
41426
41565
|
const GROK_MCP_TOOL = "MCPTool";
|
|
41427
|
-
const GROK_TOOL_TO_CATEGORY_LOWER =
|
|
41566
|
+
const GROK_TOOL_TO_CATEGORY_LOWER = {
|
|
41567
|
+
...Object.fromEntries(Object.entries(GROK_TOOL_TO_CATEGORY).map(([tool, category]) => [tool.toLowerCase(), category])),
|
|
41568
|
+
agent_message: "agent"
|
|
41569
|
+
};
|
|
41428
41570
|
const GROK_MCP_TOOL_ALIASES = /* @__PURE__ */ new Set(["mcp", GROK_MCP_TOOL.toLowerCase()]);
|
|
41429
41571
|
const GROKCLI_PERMISSION_RULES_KEY = "rules";
|
|
41430
41572
|
/**
|
|
@@ -41527,13 +41669,16 @@ function parseGrokRule(rule) {
|
|
|
41527
41669
|
* per-category, per-pattern model maps almost 1:1:
|
|
41528
41670
|
* - Generate: each `permission.<category>.<pattern> = allow|ask|deny` becomes
|
|
41529
41671
|
* the matching Grok entry and is bucketed into the `[permission]` array for
|
|
41530
|
-
* that action. `bash|read|edit|grep|webfetch|websearch` map to
|
|
41531
|
-
* tool; `write` collapses onto `Edit` (Grok has no `Write`
|
|
41532
|
-
* maps to `
|
|
41533
|
-
*
|
|
41534
|
-
*
|
|
41535
|
-
*
|
|
41536
|
-
*
|
|
41672
|
+
* that action. `bash|read|edit|grep|glob|webfetch|websearch|agent` map to
|
|
41673
|
+
* their Grok tool; `write` collapses onto `Edit` (Grok has no `Write`
|
|
41674
|
+
* tool); `glob` maps to `Glob` (an upstream alias of the `Grep` filter);
|
|
41675
|
+
* `agent` maps to `AgentMessage` (which gates messages to a running
|
|
41676
|
+
* subagent rather than its launch, and whose pattern is a subagent id
|
|
41677
|
+
* rather than a subagent type); `mcp__*` maps to `MCPTool(...)` (a scoped
|
|
41678
|
+
* MCP category folds its address into the parentheses, so a non-`*`
|
|
41679
|
+
* argument pattern on it is not represented). The one category with no Grok tool (`notebookedit`) is
|
|
41680
|
+
* skipped (with a warning when it carries a `deny` rule, to surface the
|
|
41681
|
+
* gap). When two canonical rules collapse onto the same Grok
|
|
41537
41682
|
* entry with different actions (e.g. `edit` allow + `write` deny → `Edit`),
|
|
41538
41683
|
* the strictest wins (`deny > ask > allow`) and a warning is logged, so the
|
|
41539
41684
|
* entry never lands contradictorily in two arrays.
|
|
@@ -42645,6 +42790,32 @@ function mergeKimiCodeToolsSection({ existingContent, patch }) {
|
|
|
42645
42790
|
return merged;
|
|
42646
42791
|
}
|
|
42647
42792
|
/**
|
|
42793
|
+
* Carry the existing `[permission]` siblings of `rules` over onto the generated
|
|
42794
|
+
* table. The gateway replaces the owned `permission` key wholesale and rulesync
|
|
42795
|
+
* only ever authors `rules`, so a hand-written sibling such as
|
|
42796
|
+
* `dangerous_command_guard = false` (Kimi Code 0.40.0) would otherwise be
|
|
42797
|
+
* deleted on every generate. Rulesync does not model these keys; they pass
|
|
42798
|
+
* through verbatim and `rules` always comes from the patch.
|
|
42799
|
+
*
|
|
42800
|
+
* @see https://moonshotai.github.io/kimi-code/en/configuration/config-files.html#permission
|
|
42801
|
+
*/
|
|
42802
|
+
function mergeKimiCodePermissionSection({ existingContent, patch }) {
|
|
42803
|
+
let existing;
|
|
42804
|
+
try {
|
|
42805
|
+
existing = parseSharedConfig({
|
|
42806
|
+
format: "toml",
|
|
42807
|
+
fileContent: existingContent
|
|
42808
|
+
}).permission;
|
|
42809
|
+
} catch {
|
|
42810
|
+
existing = void 0;
|
|
42811
|
+
}
|
|
42812
|
+
const { rules: _existingRules, ...siblings } = isRecord$1(existing) ? existing : {};
|
|
42813
|
+
return {
|
|
42814
|
+
...siblings,
|
|
42815
|
+
...isRecord$1(patch.permission) ? patch.permission : {}
|
|
42816
|
+
};
|
|
42817
|
+
}
|
|
42818
|
+
/**
|
|
42648
42819
|
* Build Kimi's `[tools]` section from a rulesync override, or read one back on
|
|
42649
42820
|
* import. Entries pass through verbatim: the section uses agent-file tool syntax
|
|
42650
42821
|
* (exact built-in names, `mcp__server__*` globs), not the canonical
|
|
@@ -42824,12 +42995,17 @@ var KimiCodePermissions = class KimiCodePermissions extends ToolPermissions {
|
|
|
42824
42995
|
existingContent: fileContent,
|
|
42825
42996
|
patch
|
|
42826
42997
|
});
|
|
42998
|
+
const mergedPermission = mergeKimiCodePermissionSection({
|
|
42999
|
+
existingContent: fileContent,
|
|
43000
|
+
patch
|
|
43001
|
+
});
|
|
42827
43002
|
this.fileContent = applySharedConfigPatch({
|
|
42828
43003
|
fileKey: getKimiCodeConfigSharedFileKey({ global: this.global }),
|
|
42829
43004
|
feature: "permissions",
|
|
42830
43005
|
existingContent: fileContent,
|
|
42831
43006
|
patch: {
|
|
42832
43007
|
...patch,
|
|
43008
|
+
permission: mergedPermission,
|
|
42833
43009
|
...mergedTools && { tools: mergedTools }
|
|
42834
43010
|
},
|
|
42835
43011
|
filePath: (0, node_path.join)(paths.relativeDirPath, paths.relativeFilePath)
|
|
@@ -52235,6 +52411,207 @@ var DevinSkill = class DevinSkill extends ToolSkill {
|
|
|
52235
52411
|
}
|
|
52236
52412
|
};
|
|
52237
52413
|
//#endregion
|
|
52414
|
+
//#region src/constants/dsh-paths.ts
|
|
52415
|
+
/**
|
|
52416
|
+
* DeepSeek Harness (`dsh`) configuration-layout conventions.
|
|
52417
|
+
*
|
|
52418
|
+
* The harness home is `$DSH_HOME`, which defaults to `~/.dsh`; rulesync writes
|
|
52419
|
+
* only the default location. Project-scoped assets live under a `.dsh/`
|
|
52420
|
+
* directory at the project root.
|
|
52421
|
+
*
|
|
52422
|
+
* Rules: `dsh-agent-instructions` loads the user-global `$DSH_HOME/AGENTS.md`
|
|
52423
|
+
* (root file only — the user-global file has no `.local.md` overlay), then the
|
|
52424
|
+
* project chain of `AGENTS.md` / `CLAUDE.md` files from the project root (the
|
|
52425
|
+
* nearest ancestor holding `.git`) down to the session cwd, broad to specific.
|
|
52426
|
+
* Rulesync emits `AGENTS.md` only; `CLAUDE.md` is the `claudecode` target's
|
|
52427
|
+
* file, and a `CLAUDE.md` duplicating its sibling `AGENTS.md` renders once.
|
|
52428
|
+
*
|
|
52429
|
+
* Skills: `dsh-skill-filesystem` scans `<projectRoot>/.dsh/skills` (rank 100),
|
|
52430
|
+
* `<projectRoot>/.agents/skills` (200), `<dshHome>/skills` (400) and
|
|
52431
|
+
* `<agentsHome>/skills` (500), each holding top-level `<name>/SKILL.md`
|
|
52432
|
+
* bundles or flat `<name>.md` files; nested `SKILL.md` files are deliberately
|
|
52433
|
+
* not discovered. Rulesync writes only the `dsh`-specific roots — the
|
|
52434
|
+
* `.agents/skills` roots are the `agentsskills` target's output.
|
|
52435
|
+
*
|
|
52436
|
+
* @see https://github.com/deepseek-ai/deepseek-harness
|
|
52437
|
+
* @see https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/context/agent-instructions/README.md
|
|
52438
|
+
* @see https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/skill/skill-filesystem/README.md
|
|
52439
|
+
*/
|
|
52440
|
+
/**
|
|
52441
|
+
* Project-scoped `.dsh/` directory, and the harness home relative to the home
|
|
52442
|
+
* directory (`$DSH_HOME` default).
|
|
52443
|
+
*/
|
|
52444
|
+
const DSH_DIR = ".dsh";
|
|
52445
|
+
/** Skills root, relative to the project root or the harness home. */
|
|
52446
|
+
const DSH_SKILLS_DIR_PATH = (0, node_path.join)(DSH_DIR, "skills");
|
|
52447
|
+
//#endregion
|
|
52448
|
+
//#region src/features/skills/dsh-skill.ts
|
|
52449
|
+
const DshSkillFrontmatterSchema = zod_mini.z.looseObject({
|
|
52450
|
+
name: zod_mini.z.string(),
|
|
52451
|
+
description: zod_mini.z.string(),
|
|
52452
|
+
whenToUse: zod_mini.z.optional(zod_mini.z.string()),
|
|
52453
|
+
metadata: zod_mini.z.optional(zod_mini.z.looseObject({})),
|
|
52454
|
+
"disable-model-invocation": zod_mini.z.optional(zod_mini.z.boolean()),
|
|
52455
|
+
"user-invocable": zod_mini.z.optional(zod_mini.z.boolean())
|
|
52456
|
+
});
|
|
52457
|
+
/**
|
|
52458
|
+
* Represents a DeepSeek Harness skill directory.
|
|
52459
|
+
*
|
|
52460
|
+
* The harness scans `<projectRoot>/.dsh/skills` (project) and
|
|
52461
|
+
* `~/.dsh/skills` (global, the `$DSH_HOME` default) — plus the `.agents/skills`
|
|
52462
|
+
* roots the `agentsskills` target already writes — and discovers only the
|
|
52463
|
+
* top-level entries of each root: a `<name>/SKILL.md` bundle or a flat
|
|
52464
|
+
* `<name>.md` file. Nested `SKILL.md` files below a bundle are deliberately
|
|
52465
|
+
* not discovered, which matches what rulesync emits: one top-level bundle per
|
|
52466
|
+
* skill, supporting files carried inside it as plain files.
|
|
52467
|
+
*
|
|
52468
|
+
* @see https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/skill/skill-filesystem/README.md
|
|
52469
|
+
*/
|
|
52470
|
+
var DshSkill = class DshSkill extends ToolSkill {
|
|
52471
|
+
constructor({ outputRoot = process.cwd(), relativeDirPath = DSH_SKILLS_DIR_PATH, dirName, frontmatter, body, otherFiles = [], validate = true, global = false }) {
|
|
52472
|
+
super({
|
|
52473
|
+
outputRoot,
|
|
52474
|
+
relativeDirPath,
|
|
52475
|
+
dirName,
|
|
52476
|
+
mainFile: {
|
|
52477
|
+
name: SKILL_FILE_NAME,
|
|
52478
|
+
body,
|
|
52479
|
+
frontmatter: { ...frontmatter }
|
|
52480
|
+
},
|
|
52481
|
+
otherFiles,
|
|
52482
|
+
global
|
|
52483
|
+
});
|
|
52484
|
+
if (validate) {
|
|
52485
|
+
const result = this.validate();
|
|
52486
|
+
if (!result.success) throw result.error;
|
|
52487
|
+
}
|
|
52488
|
+
}
|
|
52489
|
+
static getSettablePaths({ global: _global = false } = {}) {
|
|
52490
|
+
return { relativeDirPath: DSH_SKILLS_DIR_PATH };
|
|
52491
|
+
}
|
|
52492
|
+
getFrontmatter() {
|
|
52493
|
+
return DshSkillFrontmatterSchema.parse(this.requireMainFileFrontmatter());
|
|
52494
|
+
}
|
|
52495
|
+
getBody() {
|
|
52496
|
+
return this.mainFile?.body ?? "";
|
|
52497
|
+
}
|
|
52498
|
+
validate() {
|
|
52499
|
+
if (this.mainFile === void 0) return {
|
|
52500
|
+
success: false,
|
|
52501
|
+
error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME} file does not exist`)
|
|
52502
|
+
};
|
|
52503
|
+
const result = DshSkillFrontmatterSchema.safeParse(this.mainFile.frontmatter);
|
|
52504
|
+
if (!result.success) return {
|
|
52505
|
+
success: false,
|
|
52506
|
+
error: /* @__PURE__ */ new Error(`Invalid frontmatter in ${this.getDirPath()}: ${formatError(result.error)}`)
|
|
52507
|
+
};
|
|
52508
|
+
return {
|
|
52509
|
+
success: true,
|
|
52510
|
+
error: null
|
|
52511
|
+
};
|
|
52512
|
+
}
|
|
52513
|
+
toRulesyncSkill() {
|
|
52514
|
+
const frontmatter = this.getFrontmatter();
|
|
52515
|
+
const dshSection = {
|
|
52516
|
+
...frontmatter.whenToUse !== void 0 && { whenToUse: frontmatter.whenToUse },
|
|
52517
|
+
...frontmatter.metadata !== void 0 && { metadata: frontmatter.metadata },
|
|
52518
|
+
...frontmatter["disable-model-invocation"] !== void 0 && { "disable-model-invocation": frontmatter["disable-model-invocation"] },
|
|
52519
|
+
...frontmatter["user-invocable"] !== void 0 && { "user-invocable": frontmatter["user-invocable"] }
|
|
52520
|
+
};
|
|
52521
|
+
const rulesyncFrontmatter = {
|
|
52522
|
+
name: frontmatter.name,
|
|
52523
|
+
description: frontmatter.description,
|
|
52524
|
+
targets: ["*"],
|
|
52525
|
+
...Object.keys(dshSection).length > 0 && { dsh: dshSection }
|
|
52526
|
+
};
|
|
52527
|
+
return new RulesyncSkill({
|
|
52528
|
+
outputRoot: this.outputRoot,
|
|
52529
|
+
relativeDirPath: RULESYNC_SKILLS_RELATIVE_DIR_PATH,
|
|
52530
|
+
dirName: this.getDirName(),
|
|
52531
|
+
frontmatter: rulesyncFrontmatter,
|
|
52532
|
+
body: this.getBody(),
|
|
52533
|
+
otherFiles: this.getOtherFiles(),
|
|
52534
|
+
validate: true,
|
|
52535
|
+
global: this.global
|
|
52536
|
+
});
|
|
52537
|
+
}
|
|
52538
|
+
static fromRulesyncSkill({ outputRoot = process.cwd(), rulesyncSkill, validate = true, global = false }) {
|
|
52539
|
+
const rulesyncFrontmatter = rulesyncSkill.getFrontmatter();
|
|
52540
|
+
const dshSection = rulesyncFrontmatter.dsh;
|
|
52541
|
+
const disableModelInvocation = resolveDisableModelInvocation({
|
|
52542
|
+
rootFrontmatter: rulesyncFrontmatter,
|
|
52543
|
+
section: dshSection
|
|
52544
|
+
});
|
|
52545
|
+
const userInvocable = resolveUserInvocable({
|
|
52546
|
+
rootFrontmatter: rulesyncFrontmatter,
|
|
52547
|
+
section: dshSection
|
|
52548
|
+
});
|
|
52549
|
+
const metadata = resolveMetadata({
|
|
52550
|
+
rootFrontmatter: rulesyncFrontmatter,
|
|
52551
|
+
section: dshSection
|
|
52552
|
+
});
|
|
52553
|
+
const dshFrontmatter = {
|
|
52554
|
+
name: rulesyncFrontmatter.name,
|
|
52555
|
+
description: rulesyncFrontmatter.description,
|
|
52556
|
+
...dshSection?.whenToUse !== void 0 && { whenToUse: dshSection.whenToUse },
|
|
52557
|
+
...metadata !== void 0 && { metadata },
|
|
52558
|
+
...disableModelInvocation !== void 0 && { "disable-model-invocation": disableModelInvocation },
|
|
52559
|
+
...userInvocable !== void 0 && { "user-invocable": userInvocable }
|
|
52560
|
+
};
|
|
52561
|
+
const settablePaths = DshSkill.getSettablePaths({ global });
|
|
52562
|
+
return new DshSkill({
|
|
52563
|
+
outputRoot,
|
|
52564
|
+
relativeDirPath: settablePaths.relativeDirPath,
|
|
52565
|
+
dirName: rulesyncSkill.getDirName(),
|
|
52566
|
+
frontmatter: dshFrontmatter,
|
|
52567
|
+
body: rulesyncSkill.getBody(),
|
|
52568
|
+
otherFiles: rulesyncSkill.getOtherFiles(),
|
|
52569
|
+
validate,
|
|
52570
|
+
global
|
|
52571
|
+
});
|
|
52572
|
+
}
|
|
52573
|
+
static isTargetedByRulesyncSkill(rulesyncSkill) {
|
|
52574
|
+
const targets = rulesyncSkill.getFrontmatter().targets;
|
|
52575
|
+
return targets.includes("*") || targets.includes("dsh");
|
|
52576
|
+
}
|
|
52577
|
+
static async fromDir(params) {
|
|
52578
|
+
const loaded = await this.loadSkillDirContent({
|
|
52579
|
+
...params,
|
|
52580
|
+
getSettablePaths: DshSkill.getSettablePaths
|
|
52581
|
+
});
|
|
52582
|
+
const result = DshSkillFrontmatterSchema.safeParse(loaded.frontmatter);
|
|
52583
|
+
if (!result.success) {
|
|
52584
|
+
const skillDirPath = (0, node_path.join)(loaded.outputRoot, loaded.relativeDirPath, loaded.dirName);
|
|
52585
|
+
throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME)}: ${formatError(result.error)}`);
|
|
52586
|
+
}
|
|
52587
|
+
return new DshSkill({
|
|
52588
|
+
outputRoot: loaded.outputRoot,
|
|
52589
|
+
relativeDirPath: loaded.relativeDirPath,
|
|
52590
|
+
dirName: loaded.dirName,
|
|
52591
|
+
frontmatter: result.data,
|
|
52592
|
+
body: loaded.body,
|
|
52593
|
+
otherFiles: loaded.otherFiles,
|
|
52594
|
+
validate: true,
|
|
52595
|
+
global: loaded.global
|
|
52596
|
+
});
|
|
52597
|
+
}
|
|
52598
|
+
static forDeletion({ outputRoot = process.cwd(), relativeDirPath, dirName, global = false }) {
|
|
52599
|
+
return new DshSkill({
|
|
52600
|
+
outputRoot,
|
|
52601
|
+
relativeDirPath,
|
|
52602
|
+
dirName,
|
|
52603
|
+
frontmatter: {
|
|
52604
|
+
name: "",
|
|
52605
|
+
description: ""
|
|
52606
|
+
},
|
|
52607
|
+
body: "",
|
|
52608
|
+
otherFiles: [],
|
|
52609
|
+
validate: false,
|
|
52610
|
+
global
|
|
52611
|
+
});
|
|
52612
|
+
}
|
|
52613
|
+
};
|
|
52614
|
+
//#endregion
|
|
52238
52615
|
//#region src/features/skills/factorydroid-skill.ts
|
|
52239
52616
|
const FactorydroidSkillFrontmatterSchema = zod_mini.z.looseObject({
|
|
52240
52617
|
name: zod_mini.z.string(),
|
|
@@ -54790,6 +55167,19 @@ const VibeSkillFrontmatterSchema = zod_mini.z.looseObject({
|
|
|
54790
55167
|
"allowed-tools": zod_mini.z.optional(zod_mini.z.union([zod_mini.z.string(), zod_mini.z.array(zod_mini.z.string())]))
|
|
54791
55168
|
});
|
|
54792
55169
|
/**
|
|
55170
|
+
* Skill names Vibe's built-in skills occupy. `SkillManager` treats them as
|
|
55171
|
+
* reserved: a project or user skill whose frontmatter `name` matches one is
|
|
55172
|
+
* skipped at load time with only a debug log ("Skipping skill '<name>' ...
|
|
55173
|
+
* because builtin skill names are reserved"), so a generated skill by that
|
|
55174
|
+
* name is silently never offered. That is the legacy backend, still the
|
|
55175
|
+
* default; the unified harness constructs the manager with
|
|
55176
|
+
* `include_builtins=False` because its built-ins arrive as skills of the
|
|
55177
|
+
* shipped `vibe` plugin under namespaced names, so it reserves nothing.
|
|
55178
|
+
* @see https://github.com/mistralai/mistral-vibe/blob/v2.25.3/vibe/core/skills/builtins/__init__.py
|
|
55179
|
+
* @see https://github.com/mistralai/mistral-vibe/blob/v2.25.3/vibe/core/skills/manager.py
|
|
55180
|
+
*/
|
|
55181
|
+
const VIBE_RESERVED_SKILL_NAMES = /* @__PURE__ */ new Set(["vibe", "skill-creator"]);
|
|
55182
|
+
/**
|
|
54793
55183
|
* Build the Vibe frontmatter from a rulesync skill frontmatter, preferring the
|
|
54794
55184
|
* dedicated `vibe` section over the shared root-level fields.
|
|
54795
55185
|
*/
|
|
@@ -54821,6 +55211,15 @@ function buildVibeFrontmatter(rulesyncFrontmatter) {
|
|
|
54821
55211
|
...vibeSection?.["allowed-tools"] !== void 0 && { "allowed-tools": vibeSection["allowed-tools"] }
|
|
54822
55212
|
};
|
|
54823
55213
|
}
|
|
55214
|
+
/**
|
|
55215
|
+
* The skill is still generated under the reserved name — the name is the
|
|
55216
|
+
* user's to change, and a rename here would make it diverge from the other
|
|
55217
|
+
* targets — but Vibe would drop it without a visible word, so say so now.
|
|
55218
|
+
*/
|
|
55219
|
+
function warnAboutReservedSkillName({ name, logger }) {
|
|
55220
|
+
if (!VIBE_RESERVED_SKILL_NAMES.has(name)) return;
|
|
55221
|
+
logger?.warn(`Vibe skills: the skill name ${quoteValueForWarning(name)} is reserved for a Vibe built-in skill, so Vibe skips a project or user skill by that name when it loads skills. Rename the skill for it to be available in Vibe.`);
|
|
55222
|
+
}
|
|
54824
55223
|
var VibeSkill = class VibeSkill extends ToolSkill {
|
|
54825
55224
|
constructor({ outputRoot = process.cwd(), relativeDirPath = VibeSkill.getSettablePaths().relativeDirPath, dirName, frontmatter, body, otherFiles = [], validate = true, global = false }) {
|
|
54826
55225
|
super({
|
|
@@ -54893,9 +55292,13 @@ var VibeSkill = class VibeSkill extends ToolSkill {
|
|
|
54893
55292
|
global: this.global
|
|
54894
55293
|
});
|
|
54895
55294
|
}
|
|
54896
|
-
static fromRulesyncSkill({ outputRoot = process.cwd(), rulesyncSkill, validate = true, global = false }) {
|
|
55295
|
+
static fromRulesyncSkill({ outputRoot = process.cwd(), rulesyncSkill, validate = true, global = false, logger }) {
|
|
54897
55296
|
const settablePaths = VibeSkill.getSettablePaths({ global });
|
|
54898
55297
|
const vibeFrontmatter = buildVibeFrontmatter(rulesyncSkill.getFrontmatter());
|
|
55298
|
+
warnAboutReservedSkillName({
|
|
55299
|
+
name: vibeFrontmatter.name,
|
|
55300
|
+
logger
|
|
55301
|
+
});
|
|
54899
55302
|
return new VibeSkill({
|
|
54900
55303
|
outputRoot,
|
|
54901
55304
|
relativeDirPath: settablePaths.relativeDirPath,
|
|
@@ -55775,6 +56178,14 @@ const toolSkillFactories = /* @__PURE__ */ new Map([
|
|
|
55775
56178
|
supportsSimulated: false,
|
|
55776
56179
|
supportsGlobal: true
|
|
55777
56180
|
}
|
|
56181
|
+
}],
|
|
56182
|
+
["dsh", {
|
|
56183
|
+
class: DshSkill,
|
|
56184
|
+
meta: {
|
|
56185
|
+
supportsProject: true,
|
|
56186
|
+
supportsSimulated: false,
|
|
56187
|
+
supportsGlobal: true
|
|
56188
|
+
}
|
|
55778
56189
|
}]
|
|
55779
56190
|
]);
|
|
55780
56191
|
const defaultGetFactory$2 = (target) => {
|
|
@@ -55814,9 +56225,14 @@ var SkillsProcessor = class extends DirFeatureProcessor {
|
|
|
55814
56225
|
const rulesyncSkills = rulesyncDirs.filter((dir) => dir instanceof RulesyncSkill);
|
|
55815
56226
|
const factory = this.getFactory(this.toolTarget);
|
|
55816
56227
|
return (await Promise.all(rulesyncSkills.map(async (rulesyncSkill) => {
|
|
55817
|
-
|
|
56228
|
+
const isClaudecodeScheduledTask = rulesyncSkill.getFrontmatter().claudecode?.["scheduled-task"] === true;
|
|
56229
|
+
if (isClaudecodeScheduledTask && this.toolTarget !== "claudecode" && this.toolTarget !== "claudecode-legacy") return null;
|
|
55818
56230
|
if (!factory.class.isTargetedByRulesyncSkill(rulesyncSkill)) return null;
|
|
55819
56231
|
const dirName = rulesyncSkill.getDirName();
|
|
56232
|
+
if (isClaudecodeScheduledTask && !this.global) {
|
|
56233
|
+
this.logger.warn(`Skipping skill ${quoteForLog(dirName)} for '${this.toolTarget}': 'claudecode.scheduled-task' skills are only read from the user config directory (~/.claude/scheduled-tasks/), so nothing is written at project scope. Generate it with --global instead; a stale copy an earlier generate left under .claude/scheduled-tasks/ is removed by a generate with --delete.`);
|
|
56234
|
+
return null;
|
|
56235
|
+
}
|
|
55820
56236
|
const dirWriteBlockReason = await factory.class.getDirWriteBlockReason?.({
|
|
55821
56237
|
outputRoot: this.outputRoot,
|
|
55822
56238
|
relativeDirPath: factory.class.getSettablePaths({ global: this.global }).relativeDirPath,
|
|
@@ -56957,13 +57373,26 @@ var AntigravityPluginSubagent = class extends AntigravitySharedSubagent {
|
|
|
56957
57373
|
};
|
|
56958
57374
|
//#endregion
|
|
56959
57375
|
//#region src/features/subagents/augmentcode-subagent.ts
|
|
57376
|
+
const AugmentcodeToolListSchema = zod_mini.z.union([zod_mini.z.array(zod_mini.z.string()), zod_mini.z.string()]);
|
|
57377
|
+
/**
|
|
57378
|
+
* Normalize a documented tool-list value to the list form: a string is split on
|
|
57379
|
+
* commas, trimmed, and emptied of blank entries; a list is returned as is. A
|
|
57380
|
+
* string that names no tool (`""`, `", ,"`) is treated as unset rather than as
|
|
57381
|
+
* an empty allowlist — the author left the value blank, not the tool set — so
|
|
57382
|
+
* it yields `undefined`; an authored empty list is kept as written.
|
|
57383
|
+
*/
|
|
57384
|
+
function normalizeAugmentcodeToolList(value) {
|
|
57385
|
+
if (Array.isArray(value)) return value;
|
|
57386
|
+
const tools = value.split(",").map((tool) => tool.trim()).filter((tool) => tool.length > 0);
|
|
57387
|
+
return tools.length > 0 ? tools : void 0;
|
|
57388
|
+
}
|
|
56960
57389
|
const AugmentcodeSubagentFrontmatterSchema = zod_mini.z.looseObject({
|
|
56961
57390
|
name: zod_mini.z.string(),
|
|
56962
57391
|
description: zod_mini.z.optional(zod_mini.z.string()),
|
|
56963
57392
|
color: zod_mini.z.optional(zod_mini.z.string()),
|
|
56964
57393
|
model: zod_mini.z.optional(zod_mini.z.string()),
|
|
56965
|
-
tools: zod_mini.z.optional(
|
|
56966
|
-
disabled_tools: zod_mini.z.optional(
|
|
57394
|
+
tools: zod_mini.z.optional(AugmentcodeToolListSchema),
|
|
57395
|
+
disabled_tools: zod_mini.z.optional(AugmentcodeToolListSchema)
|
|
56967
57396
|
});
|
|
56968
57397
|
/**
|
|
56969
57398
|
* AugmentCode (Auggie CLI) subagents.
|
|
@@ -57002,14 +57431,24 @@ var AugmentcodeSubagent = class AugmentcodeSubagent extends ToolSubagent {
|
|
|
57002
57431
|
return this.body;
|
|
57003
57432
|
}
|
|
57004
57433
|
toRulesyncSubagent() {
|
|
57005
|
-
const { name, description, ...rest } = this.frontmatter;
|
|
57434
|
+
const { name, description, tools, disabled_tools, ...rest } = this.frontmatter;
|
|
57435
|
+
const normalizedTools = tools === void 0 ? void 0 : normalizeAugmentcodeToolList(tools);
|
|
57436
|
+
const normalizedDisabledTools = disabled_tools === void 0 ? void 0 : normalizeAugmentcodeToolList(disabled_tools);
|
|
57437
|
+
const toolLists = {
|
|
57438
|
+
...normalizedTools !== void 0 && { tools: normalizedTools },
|
|
57439
|
+
...normalizedDisabledTools !== void 0 && { disabled_tools: normalizedDisabledTools }
|
|
57440
|
+
};
|
|
57441
|
+
const augmentcodeSection = {
|
|
57442
|
+
...rest,
|
|
57443
|
+
...toolLists
|
|
57444
|
+
};
|
|
57006
57445
|
return new RulesyncSubagent({
|
|
57007
57446
|
outputRoot: ".",
|
|
57008
57447
|
frontmatter: {
|
|
57009
57448
|
targets: ["*"],
|
|
57010
57449
|
name,
|
|
57011
57450
|
description,
|
|
57012
|
-
...Object.keys(
|
|
57451
|
+
...Object.keys(augmentcodeSection).length > 0 && { augmentcode: augmentcodeSection }
|
|
57013
57452
|
},
|
|
57014
57453
|
body: this.body,
|
|
57015
57454
|
relativeDirPath: RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH,
|
|
@@ -63322,13 +63761,40 @@ const CodebuddyRuleFrontmatterSchema = zod_mini.z.object({
|
|
|
63322
63761
|
enabled: zod_mini.z.optional(zod_mini.z.boolean())
|
|
63323
63762
|
});
|
|
63324
63763
|
/**
|
|
63764
|
+
* Splits a scalar `paths` value on the commas that separate patterns, leaving
|
|
63765
|
+
* the commas inside a brace group alone: CodeBuddy's memory docs say "You can
|
|
63766
|
+
* also combine multiple patterns with commas" and give
|
|
63767
|
+
* `paths: {src,lib}/**\/*.ts, tests/**\/*.test.ts` as the example, where the
|
|
63768
|
+
* first comma belongs to the brace expansion and the second separates the two
|
|
63769
|
+
* globs. Only a scalar is split — the docs describe the comma form for the
|
|
63770
|
+
* string shape, and a list already separates its patterns.
|
|
63771
|
+
*/
|
|
63772
|
+
function splitCodebuddyPathsScalar(paths) {
|
|
63773
|
+
const patterns = [];
|
|
63774
|
+
let current = "";
|
|
63775
|
+
let braceDepth = 0;
|
|
63776
|
+
for (const char of paths) {
|
|
63777
|
+
if (char === "{") braceDepth += 1;
|
|
63778
|
+
else if (char === "}" && braceDepth > 0) braceDepth -= 1;
|
|
63779
|
+
else if (char === "," && braceDepth === 0) {
|
|
63780
|
+
patterns.push(current);
|
|
63781
|
+
current = "";
|
|
63782
|
+
continue;
|
|
63783
|
+
}
|
|
63784
|
+
current += char;
|
|
63785
|
+
}
|
|
63786
|
+
patterns.push(current);
|
|
63787
|
+
return patterns.map((pattern) => pattern.trim());
|
|
63788
|
+
}
|
|
63789
|
+
/**
|
|
63325
63790
|
* Normalizes the documented `string` / `string[]` shapes of `paths` to the
|
|
63326
|
-
* list form the rest of the adapter works with
|
|
63327
|
-
*
|
|
63791
|
+
* list form the rest of the adapter works with, splitting a comma-separated
|
|
63792
|
+
* scalar into its patterns (see `splitCodebuddyPathsScalar`). An empty list
|
|
63793
|
+
* and an empty string both mean "no paths".
|
|
63328
63794
|
*/
|
|
63329
63795
|
function normalizeCodebuddyPaths(paths) {
|
|
63330
63796
|
if (paths === void 0) return;
|
|
63331
|
-
const list = (typeof paths === "string" ?
|
|
63797
|
+
const list = (typeof paths === "string" ? splitCodebuddyPathsScalar(paths) : paths).filter((path) => path.trim() !== "");
|
|
63332
63798
|
return list.length > 0 ? list : void 0;
|
|
63333
63799
|
}
|
|
63334
63800
|
/**
|
|
@@ -64562,6 +65028,125 @@ var DevinRule = class DevinRule extends ToolRule {
|
|
|
64562
65028
|
}
|
|
64563
65029
|
};
|
|
64564
65030
|
//#endregion
|
|
65031
|
+
//#region src/features/rules/dsh-rule.ts
|
|
65032
|
+
/**
|
|
65033
|
+
* DeepSeek Harness (`dsh`) rules.
|
|
65034
|
+
*
|
|
65035
|
+
* Project scope writes the root `AGENTS.md` plus nested `<dir>/AGENTS.md`
|
|
65036
|
+
* files — the project chain `dsh-agent-instructions` loads from the `.git`
|
|
65037
|
+
* root down to the session cwd. Global scope writes the user-global
|
|
65038
|
+
* `~/.dsh/AGENTS.md` (`$DSH_HOME` default), which the harness reads as a
|
|
65039
|
+
* single root file with no local overlay, so non-root rules fold into it.
|
|
65040
|
+
*
|
|
65041
|
+
* @see https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/context/agent-instructions/README.md
|
|
65042
|
+
*/
|
|
65043
|
+
var DshRule = class DshRule extends ToolRule {
|
|
65044
|
+
static getSettablePaths({ global = false } = {}) {
|
|
65045
|
+
if (global) return { root: {
|
|
65046
|
+
relativeDirPath: DSH_DIR,
|
|
65047
|
+
relativeFilePath: AGENTSMD_RULE_FILE_NAME
|
|
65048
|
+
} };
|
|
65049
|
+
return { root: {
|
|
65050
|
+
relativeDirPath: ".",
|
|
65051
|
+
relativeFilePath: AGENTSMD_RULE_FILE_NAME
|
|
65052
|
+
} };
|
|
65053
|
+
}
|
|
65054
|
+
/**
|
|
65055
|
+
* DeepSeek Harness loads the project chain of `AGENTS.md` files from the
|
|
65056
|
+
* project root (the nearest ancestor holding `.git`) down through the session
|
|
65057
|
+
* cwd, broad to specific, so a per-directory file applies only while working
|
|
65058
|
+
* under that directory. Nested files are therefore a real scoping surface,
|
|
65059
|
+
* not just the root file's overflow.
|
|
65060
|
+
*
|
|
65061
|
+
* The scan mirrors the AGENTS.md standard's nested discovery — same file
|
|
65062
|
+
* name, same exclusions, import-only, project scope — because it discovers
|
|
65063
|
+
* literally the same files.
|
|
65064
|
+
* @see https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/context/agent-instructions/README.md
|
|
65065
|
+
*/
|
|
65066
|
+
static getNestedFilePatterns() {
|
|
65067
|
+
return this.buildNestedFilePatterns({ fileName: AGENTSMD_RULE_FILE_NAME });
|
|
65068
|
+
}
|
|
65069
|
+
/**
|
|
65070
|
+
* The subproject directory this rule scopes, or `undefined` for the root file
|
|
65071
|
+
* (project or global).
|
|
65072
|
+
*/
|
|
65073
|
+
getSubprojectPath() {
|
|
65074
|
+
return this.getNestedSubprojectPath({ fileName: AGENTSMD_RULE_FILE_NAME });
|
|
65075
|
+
}
|
|
65076
|
+
static async fromFile({ outputRoot = process.cwd(), relativeFilePath: _relativeFilePath, relativeDirPath: overrideDirPath, validate = true, global = false }) {
|
|
65077
|
+
const { root } = this.getSettablePaths({ global });
|
|
65078
|
+
if (overrideDirPath !== void 0 && overrideDirPath !== root.relativeDirPath && overrideDirPath !== ".") {
|
|
65079
|
+
const fileContent = await readFileContent((0, node_path.join)(outputRoot, overrideDirPath, AGENTSMD_RULE_FILE_NAME));
|
|
65080
|
+
return new DshRule({
|
|
65081
|
+
outputRoot,
|
|
65082
|
+
relativeDirPath: overrideDirPath,
|
|
65083
|
+
relativeFilePath: AGENTSMD_RULE_FILE_NAME,
|
|
65084
|
+
fileContent,
|
|
65085
|
+
validate,
|
|
65086
|
+
root: false
|
|
65087
|
+
});
|
|
65088
|
+
}
|
|
65089
|
+
const fileContent = await readFileContent((0, node_path.join)(outputRoot, root.relativeDirPath, root.relativeFilePath));
|
|
65090
|
+
return new DshRule({
|
|
65091
|
+
outputRoot,
|
|
65092
|
+
relativeDirPath: root.relativeDirPath,
|
|
65093
|
+
relativeFilePath: root.relativeFilePath,
|
|
65094
|
+
fileContent,
|
|
65095
|
+
validate,
|
|
65096
|
+
root: true
|
|
65097
|
+
});
|
|
65098
|
+
}
|
|
65099
|
+
static fromRulesyncRule({ outputRoot = process.cwd(), rulesyncRule, validate = true, global = false }) {
|
|
65100
|
+
const { root } = this.getSettablePaths({ global });
|
|
65101
|
+
const frontmatter = rulesyncRule.getFrontmatter();
|
|
65102
|
+
const isRoot = frontmatter.root ?? false;
|
|
65103
|
+
const subprojectPath = frontmatter.agentsmd?.subprojectPath;
|
|
65104
|
+
if (!global && !isRoot && subprojectPath) return new DshRule({
|
|
65105
|
+
outputRoot,
|
|
65106
|
+
relativeDirPath: (0, node_path.join)(subprojectPath),
|
|
65107
|
+
relativeFilePath: AGENTSMD_RULE_FILE_NAME,
|
|
65108
|
+
fileContent: rulesyncRule.getBody(),
|
|
65109
|
+
validate,
|
|
65110
|
+
root: false
|
|
65111
|
+
});
|
|
65112
|
+
return new DshRule({
|
|
65113
|
+
outputRoot,
|
|
65114
|
+
relativeDirPath: root.relativeDirPath,
|
|
65115
|
+
relativeFilePath: root.relativeFilePath,
|
|
65116
|
+
fileContent: rulesyncRule.getBody(),
|
|
65117
|
+
validate,
|
|
65118
|
+
root: isRoot
|
|
65119
|
+
});
|
|
65120
|
+
}
|
|
65121
|
+
toRulesyncRule() {
|
|
65122
|
+
const subprojectPath = this.getSubprojectPath();
|
|
65123
|
+
if (subprojectPath === void 0) return this.toRulesyncRuleDefault();
|
|
65124
|
+
return this.toRulesyncRuleNestedAgentsmd({ subprojectPath });
|
|
65125
|
+
}
|
|
65126
|
+
validate() {
|
|
65127
|
+
return {
|
|
65128
|
+
success: true,
|
|
65129
|
+
error: null
|
|
65130
|
+
};
|
|
65131
|
+
}
|
|
65132
|
+
static forDeletion({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath }) {
|
|
65133
|
+
return new DshRule({
|
|
65134
|
+
outputRoot,
|
|
65135
|
+
relativeDirPath,
|
|
65136
|
+
relativeFilePath,
|
|
65137
|
+
fileContent: "",
|
|
65138
|
+
validate: false,
|
|
65139
|
+
root: relativeFilePath === "AGENTS.md" && (relativeDirPath === "." || relativeDirPath === ".dsh")
|
|
65140
|
+
});
|
|
65141
|
+
}
|
|
65142
|
+
static isTargetedByRulesyncRule(rulesyncRule) {
|
|
65143
|
+
return this.isTargetedByRulesyncRuleDefault({
|
|
65144
|
+
rulesyncRule,
|
|
65145
|
+
toolTarget: "dsh"
|
|
65146
|
+
});
|
|
65147
|
+
}
|
|
65148
|
+
};
|
|
65149
|
+
//#endregion
|
|
64565
65150
|
//#region src/features/rules/factorydroid-rule.ts
|
|
64566
65151
|
/**
|
|
64567
65152
|
* Rule generator for Factory Droid.
|
|
@@ -64570,27 +65155,36 @@ var DevinRule = class DevinRule extends ToolRule {
|
|
|
64570
65155
|
* (global) as coding guidelines, plus non-root rules referenced from it via
|
|
64571
65156
|
* `.factory/rules/*.md`.
|
|
64572
65157
|
*
|
|
64573
|
-
* Factory Droid also loads
|
|
64574
|
-
* independent instruction
|
|
64575
|
-
*
|
|
64576
|
-
*
|
|
64577
|
-
* `
|
|
64578
|
-
*
|
|
64579
|
-
*
|
|
64580
|
-
* `DESIGN.md
|
|
64581
|
-
* `AGENTS.md
|
|
64582
|
-
*
|
|
65158
|
+
* Factory Droid also loads two further fixed files (project only) as
|
|
65159
|
+
* independent instruction surfaces, which rulesync emits from any non-root
|
|
65160
|
+
* rule that opts in via a `factorydroid.channel` frontmatter key. Opted-in
|
|
65161
|
+
* rule bodies are routed to the channel's file instead of
|
|
65162
|
+
* `AGENTS.md`/`.factory/rules/*.md`, and multiple opted-in rules concatenate
|
|
65163
|
+
* in source order:
|
|
65164
|
+
*
|
|
65165
|
+
* - `design` → `DESIGN.md`: "Always-on design-system, UX, visual, and
|
|
65166
|
+
* interaction guidance", loaded separately from `AGENTS.md`'s coding
|
|
65167
|
+
* guidelines. Factory's docs describe `DESIGN.md` at the repository root and
|
|
65168
|
+
* in nested subdirectories, like `AGENTS.md`, but document no
|
|
65169
|
+
* personal/global home-directory equivalent.
|
|
65170
|
+
* - `threat-model` → `.factory/threat-model.md`: the attack-surface map
|
|
65171
|
+
* Factory's Security Review reads — "if `.factory/threat-model.md` exists,
|
|
65172
|
+
* Droid uses it as the attack-surface map". It is documented only as a
|
|
65173
|
+
* repository file, so it has no global scope either.
|
|
65174
|
+
*
|
|
65175
|
+
* Both channels are therefore project scope only.
|
|
64583
65176
|
* @see https://docs.factory.ai/cli/configuration/agents-md
|
|
65177
|
+
* @see https://docs.factory.ai/software-factory/security-review
|
|
64584
65178
|
*/
|
|
64585
65179
|
var FactorydroidRule = class FactorydroidRule extends ToolRule {
|
|
64586
|
-
|
|
64587
|
-
constructor({ fileContent, root,
|
|
65180
|
+
channel;
|
|
65181
|
+
constructor({ fileContent, root, channel, ...rest }) {
|
|
64588
65182
|
super({
|
|
64589
65183
|
...rest,
|
|
64590
65184
|
fileContent,
|
|
64591
65185
|
root: root ?? false
|
|
64592
65186
|
});
|
|
64593
|
-
this.
|
|
65187
|
+
this.channel = channel;
|
|
64594
65188
|
}
|
|
64595
65189
|
static getSettablePaths({ global, excludeToolDir } = {}) {
|
|
64596
65190
|
if (global) return { root: {
|
|
@@ -64606,41 +65200,75 @@ var FactorydroidRule = class FactorydroidRule extends ToolRule {
|
|
|
64606
65200
|
design: {
|
|
64607
65201
|
relativeDirPath: ".",
|
|
64608
65202
|
relativeFilePath: FACTORYDROID_DESIGN_FILE_NAME
|
|
65203
|
+
},
|
|
65204
|
+
threatModel: {
|
|
65205
|
+
relativeDirPath: buildToolPath(FACTORYDROID_DIR, ".", excludeToolDir),
|
|
65206
|
+
relativeFilePath: FACTORYDROID_THREAT_MODEL_FILE_NAME
|
|
64609
65207
|
}
|
|
64610
65208
|
};
|
|
64611
65209
|
}
|
|
64612
65210
|
/**
|
|
65211
|
+
* The channel files in a fixed order, so that `getExtraFixedFiles` and the
|
|
65212
|
+
* channel lookups below agree on which paths are channels. Empty in global
|
|
65213
|
+
* mode, where neither file has a documented home-directory equivalent.
|
|
65214
|
+
*/
|
|
65215
|
+
static getChannelPaths({ global }) {
|
|
65216
|
+
if (global) return [];
|
|
65217
|
+
const paths = this.getSettablePaths({ global });
|
|
65218
|
+
return [{
|
|
65219
|
+
channel: "design",
|
|
65220
|
+
path: paths.design
|
|
65221
|
+
}, {
|
|
65222
|
+
channel: "threat-model",
|
|
65223
|
+
path: paths.threatModel
|
|
65224
|
+
}];
|
|
65225
|
+
}
|
|
65226
|
+
/**
|
|
65227
|
+
* Which channel, if any, owns the given output path. Matching on
|
|
65228
|
+
* `relativeDirPath` too (not just the basename) keeps a non-root rule that
|
|
65229
|
+
* happens to be named `DESIGN.md` or `threat-model.md` under
|
|
65230
|
+
* `.factory/rules/` from being routed to a channel by mistake.
|
|
65231
|
+
*/
|
|
65232
|
+
static findChannelByPath({ relativeDirPath, relativeFilePath, global }) {
|
|
65233
|
+
return this.getChannelPaths({ global }).find(({ path }) => relativeDirPath === path.relativeDirPath && relativeFilePath === path.relativeFilePath);
|
|
65234
|
+
}
|
|
65235
|
+
/**
|
|
64613
65236
|
* Extra fixed files this tool manages beyond the root/non-root rules. The
|
|
64614
65237
|
* RulesProcessor enumerates these for import and deletion so a stale
|
|
64615
|
-
* `DESIGN.md` is cleaned up once no rule opts
|
|
64616
|
-
* mode:
|
|
65238
|
+
* `DESIGN.md` or `.factory/threat-model.md` is cleaned up once no rule opts
|
|
65239
|
+
* in anymore. Empty in global mode: neither file has a documented
|
|
65240
|
+
* home-directory equivalent.
|
|
64617
65241
|
*/
|
|
64618
65242
|
static getExtraFixedFiles({ global = false } = {}) {
|
|
64619
|
-
|
|
64620
|
-
return [this.getSettablePaths({ global }).design];
|
|
65243
|
+
return this.getChannelPaths({ global }).map(({ path }) => path);
|
|
64621
65244
|
}
|
|
64622
65245
|
/**
|
|
64623
|
-
* Factory Droid loads
|
|
64624
|
-
* TOON reference section would double-load the content (and
|
|
64625
|
-
* as a rule the model must remember to open).
|
|
65246
|
+
* Factory Droid loads the channel files itself, so listing one in the root
|
|
65247
|
+
* rule's TOON reference section would double-load the content (and
|
|
65248
|
+
* misrepresent it as a rule the model must remember to open).
|
|
64626
65249
|
*/
|
|
64627
65250
|
isExcludedFromRootReferences() {
|
|
64628
|
-
return this.
|
|
65251
|
+
return this.channel !== void 0;
|
|
64629
65252
|
}
|
|
64630
65253
|
static async fromFile({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath, validate = true, global = false }) {
|
|
64631
65254
|
const paths = this.getSettablePaths({ global });
|
|
64632
|
-
const
|
|
64633
|
-
|
|
64634
|
-
|
|
65255
|
+
const channelMatch = this.findChannelByPath({
|
|
65256
|
+
relativeDirPath,
|
|
65257
|
+
relativeFilePath,
|
|
65258
|
+
global
|
|
65259
|
+
});
|
|
65260
|
+
if (channelMatch) {
|
|
65261
|
+
const { channel, path } = channelMatch;
|
|
65262
|
+
const relativePath = (0, node_path.join)(path.relativeDirPath, path.relativeFilePath);
|
|
64635
65263
|
const fileContent = await readFileContent((0, node_path.join)(outputRoot, relativePath));
|
|
64636
65264
|
return new FactorydroidRule({
|
|
64637
65265
|
outputRoot,
|
|
64638
|
-
relativeDirPath:
|
|
64639
|
-
relativeFilePath:
|
|
65266
|
+
relativeDirPath: path.relativeDirPath,
|
|
65267
|
+
relativeFilePath: path.relativeFilePath,
|
|
64640
65268
|
fileContent,
|
|
64641
65269
|
validate,
|
|
64642
65270
|
root: false,
|
|
64643
|
-
|
|
65271
|
+
channel
|
|
64644
65272
|
});
|
|
64645
65273
|
}
|
|
64646
65274
|
if (relativeFilePath === paths.root.relativeFilePath) {
|
|
@@ -64669,9 +65297,12 @@ var FactorydroidRule = class FactorydroidRule extends ToolRule {
|
|
|
64669
65297
|
}
|
|
64670
65298
|
static forDeletion({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath, global = false }) {
|
|
64671
65299
|
const paths = this.getSettablePaths({ global });
|
|
64672
|
-
const
|
|
64673
|
-
|
|
64674
|
-
|
|
65300
|
+
const channel = this.findChannelByPath({
|
|
65301
|
+
relativeDirPath,
|
|
65302
|
+
relativeFilePath,
|
|
65303
|
+
global
|
|
65304
|
+
})?.channel;
|
|
65305
|
+
const isRoot = channel === void 0 && relativeFilePath === paths.root.relativeFilePath && relativeDirPath === paths.root.relativeDirPath;
|
|
64675
65306
|
return new FactorydroidRule({
|
|
64676
65307
|
outputRoot,
|
|
64677
65308
|
relativeDirPath,
|
|
@@ -64679,22 +65310,24 @@ var FactorydroidRule = class FactorydroidRule extends ToolRule {
|
|
|
64679
65310
|
fileContent: "",
|
|
64680
65311
|
validate: false,
|
|
64681
65312
|
root: isRoot,
|
|
64682
|
-
|
|
65313
|
+
channel
|
|
64683
65314
|
});
|
|
64684
65315
|
}
|
|
64685
65316
|
static fromRulesyncRule({ outputRoot = process.cwd(), rulesyncRule, validate = true, global = false }) {
|
|
64686
65317
|
const frontmatter = rulesyncRule.getFrontmatter();
|
|
64687
65318
|
const paths = this.getSettablePaths({ global });
|
|
64688
|
-
|
|
64689
|
-
|
|
65319
|
+
const requestedChannel = frontmatter.factorydroid?.channel;
|
|
65320
|
+
const channelMatch = !global && !frontmatter.root && requestedChannel !== void 0 ? this.getChannelPaths({ global }).find(({ channel }) => channel === requestedChannel) : void 0;
|
|
65321
|
+
if (channelMatch) {
|
|
65322
|
+
const { channel, path } = channelMatch;
|
|
64690
65323
|
return new FactorydroidRule({
|
|
64691
65324
|
outputRoot,
|
|
64692
|
-
relativeDirPath:
|
|
64693
|
-
relativeFilePath:
|
|
65325
|
+
relativeDirPath: path.relativeDirPath,
|
|
65326
|
+
relativeFilePath: path.relativeFilePath,
|
|
64694
65327
|
fileContent: rulesyncRule.getBody(),
|
|
64695
65328
|
validate,
|
|
64696
65329
|
root: false,
|
|
64697
|
-
|
|
65330
|
+
channel
|
|
64698
65331
|
});
|
|
64699
65332
|
}
|
|
64700
65333
|
return new FactorydroidRule(this.buildToolRuleParamsAgentsmd({
|
|
@@ -64706,14 +65339,14 @@ var FactorydroidRule = class FactorydroidRule extends ToolRule {
|
|
|
64706
65339
|
}));
|
|
64707
65340
|
}
|
|
64708
65341
|
toRulesyncRule() {
|
|
64709
|
-
if (this.
|
|
65342
|
+
if (this.channel !== void 0) return new RulesyncRule({
|
|
64710
65343
|
outputRoot: process.cwd(),
|
|
64711
65344
|
relativeDirPath: RULESYNC_RULES_RELATIVE_DIR_PATH,
|
|
64712
|
-
relativeFilePath:
|
|
65345
|
+
relativeFilePath: this.getRelativeFilePath(),
|
|
64713
65346
|
frontmatter: {
|
|
64714
65347
|
root: false,
|
|
64715
65348
|
targets: ["factorydroid"],
|
|
64716
|
-
factorydroid: { channel:
|
|
65349
|
+
factorydroid: { channel: this.channel }
|
|
64717
65350
|
},
|
|
64718
65351
|
body: this.getFileContent()
|
|
64719
65352
|
});
|
|
@@ -67680,6 +68313,15 @@ const toolRuleFactories = /* @__PURE__ */ new Map([
|
|
|
67680
68313
|
ruleDiscoveryMode: "auto",
|
|
67681
68314
|
collisionPolicy: "fold"
|
|
67682
68315
|
}
|
|
68316
|
+
}],
|
|
68317
|
+
["dsh", {
|
|
68318
|
+
class: DshRule,
|
|
68319
|
+
meta: {
|
|
68320
|
+
extension: "md",
|
|
68321
|
+
supportsGlobal: true,
|
|
68322
|
+
ruleDiscoveryMode: "auto",
|
|
68323
|
+
collisionPolicy: "fold"
|
|
68324
|
+
}
|
|
67683
68325
|
}]
|
|
67684
68326
|
]);
|
|
67685
68327
|
const allToolTargetKeys = [...toolRuleFactories.keys()];
|