Ver código fonte

skill-bypass-permission v0.1.0: YOLO模式体检+幂等修复(hermes approvals/yolo+claude bypass), 改前备份, 合规静默

liuyuyang 1 semana atrás
commit
d150960890

+ 4 - 0
.gitignore

@@ -0,0 +1,4 @@
+node_modules/
+*.tgz
+.env
+*.bak-*

+ 21 - 0
LICENSE

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2026 fmodecn
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 54 - 0
README.md

@@ -0,0 +1,54 @@
+# skill-bypass-permission · YOLO 模式检查与设置技能
+
+> 校验并设置 Agent 容器的 **YOLO(免确认自主执行)模式**——Hermes `approvals.mode=off`、Claude Code `--dangerously-skip-permissions`、配置文件对应开关。**只检查不合规才动配置**,合规则零动作零噪音。
+
+[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
+
+## 能力
+
+- 🔍 **一键体检**:当前环境 YOLO 模式合规状态(Hermes config / Claude settings / 环境变量三处)
+- 🔧 **按需修复**:不合规时按标准模板补配置(幂等,重复执行无副作用)
+- 🤫 **静默通过**:已合规 → 输出一行 OK,不打扰后续动作
+
+## 检查项
+
+| 检查 | 位置 | 合规值 |
+|---|---|---|
+| Hermes approvals | `~/.hermes/config.yaml` 或 profile config.yaml → `approvals.mode` | `off` |
+| Hermes YOLO | 同上 `yolo: true` / 启动参数 `--yolo` | true |
+| Claude Code | `~/.claude/settings.json` → `permissions.allow` 含 `Bash(*)` 或会话带 `--dangerously-skip-permissions` | 已授权 |
+| 环境变量 | `HERMES_YOLO=1` / `CLAUDE_YOLO=1`(可选补充) | 存在即认可 |
+
+## 各工具安装
+
+### Claude Code
+```bash
+git clone https://git.fmode.cn/fmode/skill-bypass-permission.git
+cp -r skill-bypass-permission/skills/fmode-bypass-permission ~/.claude/skills/fmode-bypass-permission
+```
+
+### Codex / Gemini CLI / WorkBuddy / Hermes
+`SKILL.md` 并入各自 instructions/skills 目录(同仓库 README 各节)。
+
+## 用法
+
+```bash
+node skills/fmode-bypass-permission/scripts/checker.mjs check        # 只检查, 输出 JSON
+node skills/fmode-bypass-permission/scripts/checker.mjs enforce      # 检查+修复不合规项
+```
+
+check 输出示例:
+
+```json
+{ "hermes_approvals": "off", "hermes_yolo": true, "claude_bypass": true, "compliant": true }
+```
+
+## 安全边界
+
+- 本技能**只改 Agent 自身工具的配置文件**(hermes/claude),不触碰系统权限、sudo、防火墙
+- 集群标准 = 自主执行 + 可控可审计:YOLO 开启的同时,日志/上报(reporter)必须在线
+- 修复前自动备份原配置(`*.bak-<timestamp>`)
+
+## License
+
+MIT

+ 9 - 0
package.json

@@ -0,0 +1,9 @@
+{
+  "name": "fmode-bypass-permission",
+  "version": "0.1.0",
+  "license": "MIT",
+  "repository": {
+    "type": "git",
+    "url": "git+ssh://git@github.com/fmodecn/skill-bypass-permission.git"
+  }
+}

+ 31 - 0
skills/fmode-bypass-permission/SKILL.md

@@ -0,0 +1,31 @@
+---
+name: fmode-bypass-permission
+description: "YOLO 模式体检与设置:校验 Agent 容器的免确认自主执行配置(Hermes approvals.mode=off/yolo、Claude Code dangerously-skip-permissions),不合规才幂等修复(改前备份),合规则一行 OK 零噪音。适用:(1) 数字生命初始化后自检 (2) 集群标准化巡检 (3) 新工具接入时的执行权限配置。"
+description_en: "Check and enforce YOLO (autonomous execution) mode for agent containers: Hermes approvals.mode=off, Claude Code skip-permissions. Idempotent fix with auto-backup; silent pass when already compliant."
+---
+
+# Fmode Bypass Permission — YOLO 模式检查与设置
+
+## Overview
+
+集群标准:Agent 以 **YOLO(免确认自主执行)** 模式运行,同时关闭 approvals 交互——让 AI 有更多自主能力且可控(日志/上报在线)。本技能做两件事:**体检**(check 输出三处配置状态 JSON)与**幂等修复**(enforce 只动不合规项,改前自动备份)。
+
+## 快速用
+
+```bash
+node <skill_dir>/scripts/checker.mjs check      # → JSON: hermes_approvals/hermes_yolo/claude_bypass/compliant
+node <skill_dir>/scripts/checker.mjs enforce    # 不合规才修(备份 *.bak-<ts>),合规零动作
+```
+
+## 合规标准
+
+| 检查 | 合规值 |
+|---|---|
+| Hermes `approvals.mode` | `off` |
+| Hermes `yolo` | `true` |
+| Claude Code permissions | 含 `Bash(*)` / dangerously-skip |
+| 环境变量 | `HERMES_YOLO=1` / `CLAUDE_YOLO=1`(任一存在即认可该侧) |
+
+## 安全边界
+
+只动 Agent 工具自身配置文件(hermes/claude),不碰系统 sudo/防火墙;修复前备份;YOLO 开启同时**日志上报必须在线**(配合 agent-status-reporter 使用)。

+ 102 - 0
skills/fmode-bypass-permission/scripts/checker.mjs

@@ -0,0 +1,102 @@
+#!/usr/bin/env node
+#!/usr/bin/env node
+/**
+ * fmode-bypass-permission checker — YOLO 模式体检与幂等修复
+ * 零依赖(Node ≥18)。只动 Agent 工具自身配置,改动前备份。
+ *
+ * 用法:
+ *   node checker.mjs check     # 体检输出 JSON
+ *   node checker.mjs enforce   # 体检+修复不合规项
+ */
+import fs from 'fs';
+import os from 'os';
+import path from 'path';
+
+const HOME = os.homedir();
+const TS = new Date().toISOString().replace(/[-:T]/g, '').slice(0, 14);
+
+function readJson(p) { try { return JSON.parse(fs.readFileSync(p, 'utf8')); } catch { return null; } }
+function backup(p) { if (fs.existsSync(p)) fs.copyFileSync(p, `${p}.bak-${TS}`); }
+
+/** 探测 Hermes config.yaml 位置 */
+function hermesConfigPath() {
+  const candidates = [
+    path.join(HOME, '.hermes', 'config.yaml'),
+    '/opt/data/.hermes/config.yaml',
+    path.join(HOME, '.fmode-harness-agent', 'config', 'fmodeagent', 'config.yaml'),
+  ];
+  for (const p of candidates) if (fs.existsSync(p)) return p;
+  return null;
+}
+
+export function check() {
+  const result = { hermes_approvals: null, hermes_yolo: null, claude_bypass: null, env_yolo: !!(process.env.HERMES_YOLO || process.env.CLAUDE_YOLO) };
+  // Hermes yaml: 简易解析(不引依赖)
+  const hp = hermesConfigPath();
+  if (hp) {
+    const t = fs.readFileSync(hp, 'utf8');
+    const am = t.match(/approvals:[\s\S]{0,80}?mode:\s*(\w+)/);
+    result.hermes_approvals = am ? am[1] : null;
+    const ym = t.match(/^yolo:\s*(true|false)/m) || t.match(/^\s*yolo:\s*(true|false)/m);
+    result.hermes_yolo = ym ? ym[1] === 'true' : null;
+    result.hermes_config_path = hp;
+  }
+  // Claude settings
+  for (const p of [path.join(HOME, '.claude', 'settings.json'), path.join(HOME, '.claude', 'settings.local.json')]) {
+    const j = readJson(p);
+    if (j && j.permissions && Array.isArray(j.permissions.allow)) {
+      if (j.permissions.allow.some(x => /Bash\(\*\)|dangerously/i.test(x))) { result.claude_bypass = true; break; }
+    }
+  }
+  if (result.claude_bypass === null) {
+    // 进程参数兜底
+    try { const cmd = fs.readFileSync(`/proc/${process.ppid}/cmdline`, 'utf8'); result.claude_bypass = cmd.includes('dangerously-skip-permissions'); } catch {}
+  }
+  result.compliant = (result.hermes_approvals === 'off' || result.env_yolo) &&
+                     (result.hermes_yolo === true || result.env_yolo) &&
+                     (result.claude_bypass === true || result.env_yolo);
+  return result;
+}
+
+function fixHermes() {
+  const hp = hermesConfigPath();
+  if (!hp) { console.error('未找到 hermes config.yaml,跳过 hermes 修复'); return; }
+  backup(hp);
+  let t = fs.readFileSync(hp, 'utf8');
+  if (!/approvals:/.test(t)) t += '\napprovals:\n  mode: off\n';
+  else t = t.replace(/(approvals:[\s\S]{0,80}?mode:\s*)(\w+)/, '$1off');
+  if (!/^yolo:/m.test(t)) t += 'yolo: true\n';
+  else t = t.replace(/^yolo:\s*false/m, 'yolo: true');
+  fs.writeFileSync(hp, t);
+  console.log('已修复 hermes:', hp, '(备份 .bak-' + TS + ')');
+}
+
+function fixClaude() {
+  const dir = path.join(HOME, '.claude');
+  fs.mkdirSync(dir, { recursive: true });
+  const p = path.join(dir, 'settings.json');
+  backup(p);
+  const j = readJson(p) || {};
+  j.permissions = j.permissions || {};
+  j.permissions.allow = j.permissions.allow || [];
+  if (!j.permissions.allow.some(x => /Bash\(\*\)/.test(x))) j.permissions.allow.push('Bash(*)');
+  if (!j.permissions.allow.some(x => /dangerously/i.test(x))) j.permissions.allow.push('dangerously-skip-permissions');
+  fs.writeFileSync(p, JSON.stringify(j, null, 2));
+  console.log('已修复 claude:', p, '(备份 .bak-' + TS + ')');
+}
+
+function main() {
+  const cmd = process.argv[2] || 'check';
+  const r = check();
+  if (cmd === 'check') { console.log(JSON.stringify(r, null, 2)); return; }
+  if (cmd === 'enforce') {
+    if (r.compliant) { console.log(JSON.stringify({ ok: true, message: 'YOLO 模式已合规,零动作', ...r }, null, 2)); return; }
+    if (r.hermes_approvals !== 'off' || r.hermes_yolo !== true) fixHermes();
+    if (r.claude_bypass !== true) fixClaude();
+    console.log(JSON.stringify({ ok: true, fixed: true, after: check() }, null, 2));
+    return;
+  }
+  console.log('用法: check | enforce');
+}
+
+main();