rulesync 8.5.0 → 8.6.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.
@@ -21288,15 +21288,156 @@ function mapBashActionToDecision(action) {
21288
21288
 
21289
21289
  // src/features/permissions/geminicli-permissions.ts
21290
21290
  var import_node_path139 = require("path");
21291
+ var smolToml6 = __toESM(require("smol-toml"), 1);
21291
21292
  var import_mini72 = require("zod/mini");
21292
- var GeminiCliSettingsSchema = import_mini72.z.looseObject({
21293
- tools: import_mini72.z.optional(
21294
- import_mini72.z.looseObject({
21295
- allowed: import_mini72.z.optional(import_mini72.z.array(import_mini72.z.string())),
21296
- exclude: import_mini72.z.optional(import_mini72.z.array(import_mini72.z.string()))
21297
- })
21298
- )
21299
- });
21293
+
21294
+ // src/utils/logger.ts
21295
+ var BaseLogger = class {
21296
+ _verbose = false;
21297
+ _silent = false;
21298
+ constructor({ verbose = false, silent = false } = {}) {
21299
+ this._silent = silent;
21300
+ this._verbose = verbose && !silent;
21301
+ }
21302
+ get verbose() {
21303
+ return this._verbose;
21304
+ }
21305
+ get silent() {
21306
+ return this._silent;
21307
+ }
21308
+ configure({ verbose, silent }) {
21309
+ if (verbose && silent) {
21310
+ this._silent = false;
21311
+ if (!isEnvTest()) {
21312
+ this.onConflictingFlags();
21313
+ }
21314
+ }
21315
+ this._silent = silent;
21316
+ this._verbose = verbose && !silent;
21317
+ }
21318
+ onConflictingFlags() {
21319
+ console.warn("Both --verbose and --silent specified; --silent takes precedence");
21320
+ }
21321
+ };
21322
+ var ConsoleLogger = class extends BaseLogger {
21323
+ isSuppressed() {
21324
+ return isEnvTest() || this._silent;
21325
+ }
21326
+ get jsonMode() {
21327
+ return false;
21328
+ }
21329
+ captureData(_key, _value) {
21330
+ }
21331
+ getJsonData() {
21332
+ return {};
21333
+ }
21334
+ outputJson(_success, _error) {
21335
+ }
21336
+ info(message, ...args) {
21337
+ if (this.isSuppressed()) return;
21338
+ console.log(message, ...args);
21339
+ }
21340
+ success(message, ...args) {
21341
+ if (this.isSuppressed()) return;
21342
+ console.log(message, ...args);
21343
+ }
21344
+ warn(message, ...args) {
21345
+ if (this.isSuppressed()) return;
21346
+ console.warn(message, ...args);
21347
+ }
21348
+ // Errors are always emitted, even in silent mode
21349
+ error(message, _code, ...args) {
21350
+ if (isEnvTest()) return;
21351
+ const errorMessage = message instanceof Error ? message.message : message;
21352
+ console.error(errorMessage, ...args);
21353
+ }
21354
+ debug(message, ...args) {
21355
+ if (!this._verbose || this.isSuppressed()) return;
21356
+ console.log(message, ...args);
21357
+ }
21358
+ };
21359
+ var JsonLogger = class extends BaseLogger {
21360
+ _jsonOutputDone = false;
21361
+ _jsonData = {};
21362
+ _commandName;
21363
+ _version;
21364
+ constructor({
21365
+ command,
21366
+ version,
21367
+ verbose = false,
21368
+ silent = false
21369
+ }) {
21370
+ super({ verbose, silent });
21371
+ this._commandName = command;
21372
+ this._version = version;
21373
+ }
21374
+ // Suppress raw console.warn in JSON mode to avoid non-JSON text on stderr
21375
+ onConflictingFlags() {
21376
+ }
21377
+ get jsonMode() {
21378
+ return true;
21379
+ }
21380
+ captureData(key, value) {
21381
+ this._jsonData[key] = value;
21382
+ }
21383
+ getJsonData() {
21384
+ return { ...this._jsonData };
21385
+ }
21386
+ outputJson(success, error) {
21387
+ if (this._jsonOutputDone) return;
21388
+ this._jsonOutputDone = true;
21389
+ const output = {
21390
+ success,
21391
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
21392
+ command: this._commandName,
21393
+ version: this._version
21394
+ };
21395
+ if (success) {
21396
+ output.data = this._jsonData;
21397
+ } else if (error) {
21398
+ output.error = {
21399
+ code: error.code,
21400
+ message: error.message
21401
+ };
21402
+ if (error.details) {
21403
+ output.error.details = error.details;
21404
+ }
21405
+ if (error.stack) {
21406
+ output.error.stack = error.stack;
21407
+ }
21408
+ }
21409
+ const jsonStr = JSON.stringify(output, null, 2);
21410
+ if (success) {
21411
+ console.log(jsonStr);
21412
+ } else {
21413
+ console.error(jsonStr);
21414
+ }
21415
+ }
21416
+ info(_message, ..._args) {
21417
+ }
21418
+ success(_message, ..._args) {
21419
+ }
21420
+ warn(_message, ..._args) {
21421
+ }
21422
+ error(message, code, ..._args) {
21423
+ if (isEnvTest()) return;
21424
+ const errorMessage = message instanceof Error ? message.message : message;
21425
+ const errorInfo = {
21426
+ code: code || ErrorCodes.UNKNOWN_ERROR,
21427
+ message: errorMessage
21428
+ };
21429
+ if (this._verbose && message instanceof Error && message.stack) {
21430
+ errorInfo.stack = message.stack;
21431
+ }
21432
+ this.outputJson(false, errorInfo);
21433
+ }
21434
+ debug(_message, ..._args) {
21435
+ }
21436
+ };
21437
+
21438
+ // src/features/permissions/geminicli-permissions.ts
21439
+ var GEMINICLI_POLICY_RELATIVE_DIR_PATH = (0, import_node_path139.join)(".gemini", "policies");
21440
+ var GEMINICLI_POLICY_FILE_NAME = "rulesync.toml";
21300
21441
  var RULESYNC_TO_GEMINICLI_TOOL_NAME = {
21301
21442
  bash: "run_shell_command",
21302
21443
  read: "read_file",
@@ -21304,16 +21445,32 @@ var RULESYNC_TO_GEMINICLI_TOOL_NAME = {
21304
21445
  write: "write_file",
21305
21446
  webfetch: "web_fetch"
21306
21447
  };
21448
+ var GEMINICLI_TO_RULESYNC_TOOL_NAME = Object.fromEntries(
21449
+ Object.entries(RULESYNC_TO_GEMINICLI_TOOL_NAME).map(([k, v]) => [v, k])
21450
+ );
21451
+ var PRIORITY_DENY = 1e6;
21452
+ var PRIORITY_ASK = 1e3;
21453
+ var PRIORITY_ALLOW = 1;
21454
+ var SINGLE_STAR_REGEX = '[^/\\"]*';
21455
+ var DOUBLE_STAR_REGEX = '[^\\"]*';
21456
+ var SINGLE_CHAR_REGEX = '[^/\\"]';
21457
+ var LEGACY_SINGLE_STAR_REGEX = '[^\\"]*';
21458
+ var LEGACY_DOUBLE_STAR_REGEX = ".*";
21459
+ var COMMAND_ARGS_ANCHOR = '"command":"';
21460
+ var VALUE_END_ANCHOR = '\\"';
21461
+ var RESERVED_OBJECT_KEYS = /* @__PURE__ */ new Set([
21462
+ "__proto__",
21463
+ "constructor",
21464
+ "prototype"
21465
+ ]);
21466
+ var moduleLogger = new ConsoleLogger();
21307
21467
  var GeminicliPermissions = class _GeminicliPermissions extends ToolPermissions {
21308
21468
  static getSettablePaths(_options = {}) {
21309
21469
  return {
21310
- relativeDirPath: ".gemini",
21311
- relativeFilePath: "settings.json"
21470
+ relativeDirPath: GEMINICLI_POLICY_RELATIVE_DIR_PATH,
21471
+ relativeFilePath: GEMINICLI_POLICY_FILE_NAME
21312
21472
  };
21313
21473
  }
21314
- isDeletable() {
21315
- return false;
21316
- }
21317
21474
  static async fromFile({
21318
21475
  baseDir = process.cwd(),
21319
21476
  validate = true,
@@ -21321,7 +21478,7 @@ var GeminicliPermissions = class _GeminicliPermissions extends ToolPermissions {
21321
21478
  }) {
21322
21479
  const paths = this.getSettablePaths({ global });
21323
21480
  const filePath = (0, import_node_path139.join)(baseDir, paths.relativeDirPath, paths.relativeFilePath);
21324
- const fileContent = await readFileContentOrNull(filePath) ?? JSON.stringify({}, null, 2);
21481
+ const fileContent = await readFileContentOrNull(filePath) ?? "";
21325
21482
  return new _GeminicliPermissions({
21326
21483
  baseDir,
21327
21484
  relativeDirPath: paths.relativeDirPath,
@@ -21330,63 +21487,72 @@ var GeminicliPermissions = class _GeminicliPermissions extends ToolPermissions {
21330
21487
  validate
21331
21488
  });
21332
21489
  }
21333
- static async fromRulesyncPermissions({
21490
+ static fromRulesyncPermissions({
21334
21491
  baseDir = process.cwd(),
21335
21492
  rulesyncPermissions,
21336
21493
  validate = true,
21337
- logger: logger5,
21338
- global = false
21494
+ global = false,
21495
+ logger: logger5 = moduleLogger
21339
21496
  }) {
21340
21497
  const paths = this.getSettablePaths({ global });
21341
- const filePath = (0, import_node_path139.join)(baseDir, paths.relativeDirPath, paths.relativeFilePath);
21342
- const existingContent = await readFileContentOrNull(filePath) ?? JSON.stringify({}, null, 2);
21343
- const settingsResult = GeminiCliSettingsSchema.safeParse(JSON.parse(existingContent));
21344
- if (!settingsResult.success) {
21345
- throw new Error(
21346
- `Failed to parse existing Gemini CLI settings at ${filePath}: ${formatError(settingsResult.error)}`
21347
- );
21348
- }
21349
- const { allowed, exclude } = convertRulesyncToGeminicliTools({
21350
- config: rulesyncPermissions.getJson(),
21351
- logger: logger5
21352
- });
21353
- const merged = {
21354
- ...settingsResult.data,
21355
- tools: {
21356
- ...settingsResult.data.tools,
21357
- ...allowed.length > 0 ? { allowed } : {},
21358
- ...exclude.length > 0 ? { exclude } : {}
21359
- }
21360
- };
21498
+ const fileContent = buildGeminicliPolicyContent(rulesyncPermissions.getJson(), logger5);
21361
21499
  return new _GeminicliPermissions({
21362
21500
  baseDir,
21363
21501
  relativeDirPath: paths.relativeDirPath,
21364
21502
  relativeFilePath: paths.relativeFilePath,
21365
- fileContent: JSON.stringify(merged, null, 2),
21503
+ fileContent,
21366
21504
  validate
21367
21505
  });
21368
21506
  }
21369
21507
  toRulesyncPermissions() {
21370
- let settings;
21371
- try {
21372
- const parsed = JSON.parse(this.getFileContent());
21373
- settings = GeminiCliSettingsSchema.parse(parsed);
21374
- } catch (error) {
21375
- throw new Error(
21376
- `Failed to parse Gemini CLI permissions content in ${(0, import_node_path139.join)(this.getRelativeDirPath(), this.getRelativeFilePath())}: ${formatError(error)}`,
21377
- { cause: error }
21378
- );
21379
- }
21380
21508
  const permission = {};
21381
- for (const toolEntry of settings.tools?.allowed ?? []) {
21382
- const mapped = parseGeminicliToolEntry({ entry: toolEntry });
21383
- const rules = permission[mapped.category] ??= {};
21384
- rules[mapped.pattern] = "allow";
21385
- }
21386
- for (const toolEntry of settings.tools?.exclude ?? []) {
21387
- const mapped = parseGeminicliToolEntry({ entry: toolEntry });
21388
- const rules = permission[mapped.category] ??= {};
21389
- rules[mapped.pattern] = "deny";
21509
+ const fileContent = this.getFileContent();
21510
+ if (fileContent.trim().length > 0) {
21511
+ let parsed;
21512
+ try {
21513
+ parsed = smolToml6.parse(fileContent);
21514
+ } catch (error) {
21515
+ throw new Error(
21516
+ `Failed to parse Gemini CLI policy TOML in ${(0, import_node_path139.join)(this.getRelativeDirPath(), this.getRelativeFilePath())}: ${formatError(error)}`,
21517
+ { cause: error }
21518
+ );
21519
+ }
21520
+ const rules = extractRules(parsed, moduleLogger);
21521
+ for (const [index, rule] of rules.entries()) {
21522
+ const mappedCategory = Object.hasOwn(GEMINICLI_TO_RULESYNC_TOOL_NAME, rule.toolName) ? GEMINICLI_TO_RULESYNC_TOOL_NAME[rule.toolName] : void 0;
21523
+ const category = mappedCategory ?? rule.toolName;
21524
+ if (RESERVED_OBJECT_KEYS.has(category)) {
21525
+ moduleLogger.warn(
21526
+ `Skipping rule #${index} in ${this.getRelativeFilePath()}: toolName "${rule.toolName}" maps to a reserved object key ("${category}") and would risk prototype pollution.`
21527
+ );
21528
+ continue;
21529
+ }
21530
+ const action = mapFromGeminicliDecision(rule.decision);
21531
+ if (!action) {
21532
+ moduleLogger.warn(
21533
+ `Skipping rule #${index} (toolName="${rule.toolName}", commandPrefix=${JSON.stringify(rule.commandPrefix)}, argsPattern=${JSON.stringify(rule.argsPattern)}) in ${this.getRelativeFilePath()}: unknown decision ${JSON.stringify(rule.decision)}`
21534
+ );
21535
+ continue;
21536
+ }
21537
+ if (rule.toolName === "run_shell_command" && rule.commandPrefix !== void 0 && rule.argsPattern !== void 0) {
21538
+ moduleLogger.warn(
21539
+ `Rule #${index} in ${this.getRelativeFilePath()} sets both commandPrefix and argsPattern; rulesync will honor argsPattern and ignore commandPrefix=${JSON.stringify(rule.commandPrefix)}.`
21540
+ );
21541
+ }
21542
+ const pattern = extractPattern(rule);
21543
+ if (RESERVED_OBJECT_KEYS.has(pattern)) {
21544
+ moduleLogger.warn(
21545
+ `Skipping rule #${index} in ${this.getRelativeFilePath()}: pattern "${pattern}" is a reserved object key.`
21546
+ );
21547
+ continue;
21548
+ }
21549
+ const existing = Object.hasOwn(permission, category) ? permission[category] : void 0;
21550
+ const target = existing ?? {};
21551
+ if (existing === void 0) {
21552
+ permission[category] = target;
21553
+ }
21554
+ target[pattern] = action;
21555
+ }
21390
21556
  }
21391
21557
  return this.toRulesyncPermissionsDefault({
21392
21558
  fileContent: JSON.stringify({ permission }, null, 2)
@@ -21404,50 +21570,238 @@ var GeminicliPermissions = class _GeminicliPermissions extends ToolPermissions {
21404
21570
  baseDir,
21405
21571
  relativeDirPath,
21406
21572
  relativeFilePath,
21407
- fileContent: JSON.stringify({}, null, 2),
21573
+ fileContent: "",
21408
21574
  validate: false
21409
21575
  });
21410
21576
  }
21411
21577
  };
21412
- function convertRulesyncToGeminicliTools({
21413
- config,
21414
- logger: logger5
21415
- }) {
21416
- const allowed = [];
21417
- const exclude = [];
21418
- for (const [toolName, rules] of Object.entries(config.permission)) {
21578
+ function buildGeminicliPolicyContent(config, logger5) {
21579
+ const rules = [];
21580
+ let order = 0;
21581
+ for (const [toolName, entries] of Object.entries(config.permission)) {
21419
21582
  const mappedToolName = RULESYNC_TO_GEMINICLI_TOOL_NAME[toolName] ?? toolName;
21420
- if (!RULESYNC_TO_GEMINICLI_TOOL_NAME[toolName]) {
21421
- logger5?.warn(`Gemini CLI permissions use direct tool names. Mapping as-is: ${toolName}`);
21422
- }
21423
- for (const [pattern, action] of Object.entries(rules)) {
21424
- if (action === "ask") {
21425
- logger5?.warn(
21426
- `Gemini CLI does not support explicit "ask" rules in settings. Skipping ${toolName}:${pattern}`
21583
+ for (const [pattern, action] of Object.entries(entries)) {
21584
+ if (pattern === "") {
21585
+ logger5.warn(
21586
+ `Skipping rule "${toolName}: "": empty pattern is not a valid permission target and would silently match every invocation (bash) or nothing (other tools).`
21427
21587
  );
21428
21588
  continue;
21429
21589
  }
21430
- const geminiEntry = pattern === "*" ? mappedToolName : `${mappedToolName}(${pattern})`;
21431
- if (action === "allow") {
21432
- allowed.push(geminiEntry);
21433
- } else {
21434
- exclude.push(geminiEntry);
21590
+ if (hasUnsafeAnchorChar(pattern)) {
21591
+ logger5.warn(
21592
+ `Skipping rule "${toolName}: ${pattern}": pattern contains a character (" or \\) that would break JSON-anchor matching in the Gemini CLI Policy Engine.`
21593
+ );
21594
+ continue;
21595
+ }
21596
+ const decision = mapToGeminicliDecision(action);
21597
+ if (mappedToolName === "run_shell_command" && (pattern === "*" || pattern === "**") && decision !== "ask_user") {
21598
+ logger5.warn(
21599
+ `Skipping rule "${toolName}: ${pattern}" with decision ${decision}: bash match-all patterns are only supported with "ask" because they would otherwise affect every shell command.`
21600
+ );
21601
+ continue;
21602
+ }
21603
+ const currentRule = {
21604
+ toolName: mappedToolName,
21605
+ decision,
21606
+ priority: priorityForDecision(decision)
21607
+ };
21608
+ if (mappedToolName === "run_shell_command") {
21609
+ applyShellPattern({ rule: currentRule, pattern, toolName, logger: logger5 });
21610
+ } else if (pattern !== "*") {
21611
+ currentRule.argsPattern = buildNonShellArgsPattern(pattern);
21435
21612
  }
21613
+ rules.push({ rule: currentRule, order: order++ });
21436
21614
  }
21437
21615
  }
21438
- return { allowed, exclude };
21616
+ rules.sort((a, b) => {
21617
+ const diff = toNumber(b.rule.priority) - toNumber(a.rule.priority);
21618
+ return diff !== 0 ? diff : a.order - b.order;
21619
+ });
21620
+ return smolToml6.stringify({ rule: rules.map((entry) => entry.rule) });
21439
21621
  }
21440
- function parseGeminicliToolEntry({ entry }) {
21441
- const match = /^([^()]+?)(?:\((.*)\))?$/.exec(entry);
21442
- if (!match) return { category: entry, pattern: "*" };
21443
- const rawToolName = match[1]?.trim() ?? entry;
21444
- const mappedCategory = Object.entries(RULESYNC_TO_GEMINICLI_TOOL_NAME).find(
21445
- ([, value]) => value === rawToolName
21446
- )?.[0];
21447
- return {
21448
- category: mappedCategory ?? rawToolName,
21449
- pattern: (match[2] ?? "*").trim()
21450
- };
21622
+ function buildNonShellArgsPattern(pattern) {
21623
+ return `"${globPatternToRegex(pattern)}${VALUE_END_ANCHOR}`;
21624
+ }
21625
+ function hasUnsafeAnchorChar(pattern) {
21626
+ return pattern.includes('"') || pattern.includes("\\");
21627
+ }
21628
+ function toNumber(value) {
21629
+ return typeof value === "number" ? value : 0;
21630
+ }
21631
+ function applyShellPattern({
21632
+ rule,
21633
+ pattern,
21634
+ toolName,
21635
+ logger: logger5
21636
+ }) {
21637
+ if (pattern === "*") {
21638
+ return;
21639
+ }
21640
+ const trailingWildcardStripped = pattern.endsWith(" *") ? pattern.slice(0, -2) : pattern;
21641
+ if (hasGlobMetacharacter(trailingWildcardStripped)) {
21642
+ rule.argsPattern = `${COMMAND_ARGS_ANCHOR}${globPatternToRegex(pattern)}`;
21643
+ logger5.warn(
21644
+ `Gemini CLI does not support glob metacharacters inside a bash command prefix; emitting argsPattern for rule "${toolName}: ${pattern}".`
21645
+ );
21646
+ return;
21647
+ }
21648
+ rule.commandPrefix = trailingWildcardStripped;
21649
+ }
21650
+ function hasGlobMetacharacter(pattern) {
21651
+ return /[*?[\]]/.test(pattern);
21652
+ }
21653
+ function priorityForDecision(decision) {
21654
+ if (decision === "deny") return PRIORITY_DENY;
21655
+ if (decision === "ask_user") return PRIORITY_ASK;
21656
+ return PRIORITY_ALLOW;
21657
+ }
21658
+ function mapToGeminicliDecision(action) {
21659
+ if (action === "ask") {
21660
+ return "ask_user";
21661
+ }
21662
+ return action;
21663
+ }
21664
+ function mapFromGeminicliDecision(decision) {
21665
+ if (decision === "allow") return "allow";
21666
+ if (decision === "deny") return "deny";
21667
+ if (decision === "ask_user") return "ask";
21668
+ return null;
21669
+ }
21670
+ function globPatternToRegex(pattern) {
21671
+ let regex = "";
21672
+ let i = 0;
21673
+ while (i < pattern.length) {
21674
+ const char = pattern[i];
21675
+ if (char === void 0) {
21676
+ break;
21677
+ }
21678
+ if (char === "*" && pattern[i + 1] === "*") {
21679
+ regex += DOUBLE_STAR_REGEX;
21680
+ i += 2;
21681
+ continue;
21682
+ }
21683
+ if (char === "*") {
21684
+ regex += SINGLE_STAR_REGEX;
21685
+ i += 1;
21686
+ continue;
21687
+ }
21688
+ if (char === "?") {
21689
+ regex += SINGLE_CHAR_REGEX;
21690
+ i += 1;
21691
+ continue;
21692
+ }
21693
+ if (char === "[") {
21694
+ regex += escapeRegexChar(char);
21695
+ i += 1;
21696
+ continue;
21697
+ }
21698
+ if (char === "]") {
21699
+ regex += escapeRegexChar(char);
21700
+ i += 1;
21701
+ continue;
21702
+ }
21703
+ if (isRegexMetacharacter(char)) {
21704
+ regex += `\\${char}`;
21705
+ i += 1;
21706
+ continue;
21707
+ }
21708
+ regex += char;
21709
+ i += 1;
21710
+ }
21711
+ return regex;
21712
+ }
21713
+ function escapeRegexChar(char) {
21714
+ return `\\${char}`;
21715
+ }
21716
+ function isRegexMetacharacter(char) {
21717
+ return /[.+^${}()|\\]/.test(char);
21718
+ }
21719
+ function regexToGlobPattern(regex) {
21720
+ let source = regex;
21721
+ if (source.endsWith(VALUE_END_ANCHOR)) {
21722
+ source = source.slice(0, -VALUE_END_ANCHOR.length);
21723
+ }
21724
+ let glob = "";
21725
+ let i = 0;
21726
+ while (i < source.length) {
21727
+ if (source.startsWith(DOUBLE_STAR_REGEX, i)) {
21728
+ glob += "**";
21729
+ i += DOUBLE_STAR_REGEX.length;
21730
+ continue;
21731
+ }
21732
+ if (source.startsWith(LEGACY_DOUBLE_STAR_REGEX, i)) {
21733
+ glob += "**";
21734
+ i += LEGACY_DOUBLE_STAR_REGEX.length;
21735
+ continue;
21736
+ }
21737
+ if (source.startsWith(SINGLE_STAR_REGEX, i)) {
21738
+ glob += "*";
21739
+ i += SINGLE_STAR_REGEX.length;
21740
+ continue;
21741
+ }
21742
+ if (source.startsWith(LEGACY_SINGLE_STAR_REGEX, i)) {
21743
+ glob += "*";
21744
+ i += LEGACY_SINGLE_STAR_REGEX.length;
21745
+ continue;
21746
+ }
21747
+ if (source.startsWith(SINGLE_CHAR_REGEX, i)) {
21748
+ glob += "?";
21749
+ i += SINGLE_CHAR_REGEX.length;
21750
+ continue;
21751
+ }
21752
+ const char = source[i];
21753
+ if (char === "\\") {
21754
+ const escaped = source[i + 1];
21755
+ if (escaped !== void 0) {
21756
+ glob += escaped;
21757
+ i += 2;
21758
+ continue;
21759
+ }
21760
+ }
21761
+ glob += char ?? "";
21762
+ i += 1;
21763
+ }
21764
+ return glob;
21765
+ }
21766
+ var GeminicliPolicyRuleSchema = import_mini72.z.looseObject({
21767
+ toolName: import_mini72.z.string(),
21768
+ decision: import_mini72.z.optional(import_mini72.z.unknown()),
21769
+ commandPrefix: import_mini72.z.optional(import_mini72.z.string()),
21770
+ argsPattern: import_mini72.z.optional(import_mini72.z.string())
21771
+ });
21772
+ var GeminicliPolicyFileSchema = import_mini72.z.looseObject({
21773
+ rule: import_mini72.z.optional(import_mini72.z.array(import_mini72.z.looseObject({})))
21774
+ });
21775
+ function extractRules(parsed, logger5) {
21776
+ const parsedFile = GeminicliPolicyFileSchema.safeParse(parsed);
21777
+ if (!parsedFile.success || !parsedFile.data.rule) {
21778
+ return [];
21779
+ }
21780
+ const rules = [];
21781
+ for (const [index, entry] of parsedFile.data.rule.entries()) {
21782
+ const result = GeminicliPolicyRuleSchema.safeParse(entry);
21783
+ if (result.success) {
21784
+ rules.push(result.data);
21785
+ continue;
21786
+ }
21787
+ logger5.warn(
21788
+ `Skipping malformed Gemini CLI policy rule at index ${index}: ${formatError(result.error)}`
21789
+ );
21790
+ }
21791
+ return rules;
21792
+ }
21793
+ function extractPattern(rule) {
21794
+ if (rule.toolName === "run_shell_command") {
21795
+ if (rule.argsPattern) {
21796
+ const stripped = rule.argsPattern.startsWith(COMMAND_ARGS_ANCHOR) ? rule.argsPattern.slice(COMMAND_ARGS_ANCHOR.length) : rule.argsPattern;
21797
+ return regexToGlobPattern(stripped);
21798
+ }
21799
+ if (!rule.commandPrefix) return "*";
21800
+ return rule.commandPrefix.endsWith(" *") || rule.commandPrefix.endsWith("*") ? rule.commandPrefix : `${rule.commandPrefix} *`;
21801
+ }
21802
+ if (!rule.argsPattern) return "*";
21803
+ const regex = rule.argsPattern.startsWith('"') ? rule.argsPattern.slice(1) : rule.argsPattern;
21804
+ return regexToGlobPattern(regex);
21451
21805
  }
21452
21806
 
21453
21807
  // src/features/permissions/kiro-permissions.ts
@@ -24361,152 +24715,6 @@ var import_mini85 = require("zod/mini");
24361
24715
  // src/mcp/commands.ts
24362
24716
  var import_node_path148 = require("path");
24363
24717
  var import_mini77 = require("zod/mini");
24364
-
24365
- // src/utils/logger.ts
24366
- var BaseLogger = class {
24367
- _verbose = false;
24368
- _silent = false;
24369
- constructor({ verbose = false, silent = false } = {}) {
24370
- this._silent = silent;
24371
- this._verbose = verbose && !silent;
24372
- }
24373
- get verbose() {
24374
- return this._verbose;
24375
- }
24376
- get silent() {
24377
- return this._silent;
24378
- }
24379
- configure({ verbose, silent }) {
24380
- if (verbose && silent) {
24381
- this._silent = false;
24382
- if (!isEnvTest()) {
24383
- this.onConflictingFlags();
24384
- }
24385
- }
24386
- this._silent = silent;
24387
- this._verbose = verbose && !silent;
24388
- }
24389
- onConflictingFlags() {
24390
- console.warn("Both --verbose and --silent specified; --silent takes precedence");
24391
- }
24392
- };
24393
- var ConsoleLogger = class extends BaseLogger {
24394
- isSuppressed() {
24395
- return isEnvTest() || this._silent;
24396
- }
24397
- get jsonMode() {
24398
- return false;
24399
- }
24400
- captureData(_key, _value) {
24401
- }
24402
- getJsonData() {
24403
- return {};
24404
- }
24405
- outputJson(_success, _error) {
24406
- }
24407
- info(message, ...args) {
24408
- if (this.isSuppressed()) return;
24409
- console.log(message, ...args);
24410
- }
24411
- success(message, ...args) {
24412
- if (this.isSuppressed()) return;
24413
- console.log(message, ...args);
24414
- }
24415
- warn(message, ...args) {
24416
- if (this.isSuppressed()) return;
24417
- console.warn(message, ...args);
24418
- }
24419
- // Errors are always emitted, even in silent mode
24420
- error(message, _code, ...args) {
24421
- if (isEnvTest()) return;
24422
- const errorMessage = message instanceof Error ? message.message : message;
24423
- console.error(errorMessage, ...args);
24424
- }
24425
- debug(message, ...args) {
24426
- if (!this._verbose || this.isSuppressed()) return;
24427
- console.log(message, ...args);
24428
- }
24429
- };
24430
- var JsonLogger = class extends BaseLogger {
24431
- _jsonOutputDone = false;
24432
- _jsonData = {};
24433
- _commandName;
24434
- _version;
24435
- constructor({
24436
- command,
24437
- version,
24438
- verbose = false,
24439
- silent = false
24440
- }) {
24441
- super({ verbose, silent });
24442
- this._commandName = command;
24443
- this._version = version;
24444
- }
24445
- // Suppress raw console.warn in JSON mode to avoid non-JSON text on stderr
24446
- onConflictingFlags() {
24447
- }
24448
- get jsonMode() {
24449
- return true;
24450
- }
24451
- captureData(key, value) {
24452
- this._jsonData[key] = value;
24453
- }
24454
- getJsonData() {
24455
- return { ...this._jsonData };
24456
- }
24457
- outputJson(success, error) {
24458
- if (this._jsonOutputDone) return;
24459
- this._jsonOutputDone = true;
24460
- const output = {
24461
- success,
24462
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
24463
- command: this._commandName,
24464
- version: this._version
24465
- };
24466
- if (success) {
24467
- output.data = this._jsonData;
24468
- } else if (error) {
24469
- output.error = {
24470
- code: error.code,
24471
- message: error.message
24472
- };
24473
- if (error.details) {
24474
- output.error.details = error.details;
24475
- }
24476
- if (error.stack) {
24477
- output.error.stack = error.stack;
24478
- }
24479
- }
24480
- const jsonStr = JSON.stringify(output, null, 2);
24481
- if (success) {
24482
- console.log(jsonStr);
24483
- } else {
24484
- console.error(jsonStr);
24485
- }
24486
- }
24487
- info(_message, ..._args) {
24488
- }
24489
- success(_message, ..._args) {
24490
- }
24491
- warn(_message, ..._args) {
24492
- }
24493
- error(message, code, ..._args) {
24494
- if (isEnvTest()) return;
24495
- const errorMessage = message instanceof Error ? message.message : message;
24496
- const errorInfo = {
24497
- code: code || ErrorCodes.UNKNOWN_ERROR,
24498
- message: errorMessage
24499
- };
24500
- if (this._verbose && message instanceof Error && message.stack) {
24501
- errorInfo.stack = message.stack;
24502
- }
24503
- this.outputJson(false, errorInfo);
24504
- }
24505
- debug(_message, ..._args) {
24506
- }
24507
- };
24508
-
24509
- // src/mcp/commands.ts
24510
24718
  var logger = new ConsoleLogger({ verbose: false, silent: true });
24511
24719
  var maxCommandSizeBytes = 1024 * 1024;
24512
24720
  var maxCommandsCount = 1e3;
@@ -26362,7 +26570,7 @@ function wrapCommand({
26362
26570
  }
26363
26571
 
26364
26572
  // src/cli/index.ts
26365
- var getVersion = () => "8.5.0";
26573
+ var getVersion = () => "8.6.0";
26366
26574
  function wrapCommand2(name, errorCode, handler) {
26367
26575
  return wrapCommand({ name, errorCode, handler, getVersion });
26368
26576
  }