dsh-permission-rules 0.2.0 → 0.4.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/CHANGELOG.md +38 -0
- package/README.es.md +11 -6
- package/README.hi.md +11 -6
- package/README.md +11 -6
- package/README.pt.md +11 -6
- package/README.zh.md +11 -6
- package/docs/rules-format.en.md +7 -2
- package/docs/rules-format.md +7 -2
- package/docs/rules-format.schema.json +5 -0
- package/lib/index.js +467 -128
- package/lib/types/config.d.ts +12 -2
- package/lib/types/config.d.ts.map +1 -1
- package/lib/types/config.js +26 -2
- package/lib/types/config.js.map +1 -1
- package/lib/types/events.d.ts +14 -1
- package/lib/types/events.d.ts.map +1 -1
- package/lib/types/events.js +5 -0
- package/lib/types/events.js.map +1 -1
- package/lib/types/index.d.ts +8 -6
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/index.js +8 -6
- package/lib/types/index.js.map +1 -1
- package/lib/types/prose.d.ts +13 -2
- package/lib/types/prose.d.ts.map +1 -1
- package/lib/types/prose.js +50 -25
- package/lib/types/prose.js.map +1 -1
- package/lib/types/rules.d.ts +25 -2
- package/lib/types/rules.d.ts.map +1 -1
- package/lib/types/rules.js +37 -8
- package/lib/types/rules.js.map +1 -1
- package/lib/types/runtime.d.ts +84 -17
- package/lib/types/runtime.d.ts.map +1 -1
- package/lib/types/runtime.js +326 -71
- package/lib/types/runtime.js.map +1 -1
- package/package.json +103 -103
- package/src/config.ts +36 -2
- package/src/events.ts +14 -1
- package/src/index.ts +8 -6
- package/src/prose.ts +63 -27
- package/src/rules.ts +53 -8
- package/src/runtime.ts +320 -67
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,44 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to dsh-permission-rules are recorded here, newest first.
|
|
4
4
|
|
|
5
|
+
## v0.4.0 — 2026-08-15
|
|
6
|
+
|
|
7
|
+
### Hot reload and workspace identity
|
|
8
|
+
|
|
9
|
+
- **Mid-session rule-file creation is adopted automatically.** Expected-but-absent rule files (the project file when it is not in effect, a fallback deleted after mount) are now watched through their deepest existing ancestor directory (chokidar cannot reliably watch a missing path whose parent is also missing), so creating `.dsh/rules.yaml` — or recreating it after a fallback took over — takes effect without a manual `/rules reload`. Under `searchUp`, only the immediate cwd-level candidate is watched; deeper ancestors still need a reload.
|
|
10
|
+
- The per-workspace cache key is now the resolved cwd, case-folded on Windows: differently-spelled paths to the same workspace share one cache entry and one watcher set instead of doubling both.
|
|
11
|
+
|
|
12
|
+
### Commands and observability
|
|
13
|
+
|
|
14
|
+
- `/rules test` accepts `--platform <name>` (one of the closed platform list): the `when.platform` match dimension is now dry-testable on any host, completing the flag coverage of every match dimension.
|
|
15
|
+
- `/rules` lists each rule with its own source file in multi-file chains (`searchUp`), via a localized `src:` attribution token; `/rules list` is an explicit alias for the bare listing.
|
|
16
|
+
- All five output languages carry the new `testBadPlatform` message, the updated usage strings, and the `src` token.
|
|
17
|
+
|
|
18
|
+
### Engineering
|
|
19
|
+
|
|
20
|
+
- 133 tests across 8 suites (new coverage: creation-adoption watch paths, fallback recreation, cache-key dedup, `--platform`, `list`, source attribution); coverage gate held. Five-language READMEs, both rules-format references, AGENTS.md, and the `/rules` command hint updated.
|
|
21
|
+
|
|
22
|
+
## v0.3.0 — 2026-08-14
|
|
23
|
+
|
|
24
|
+
### Rules vocabulary
|
|
25
|
+
|
|
26
|
+
- New `match.agents` dimension: selector globs (`main`, `subagent`, `preset:<name>`) matched against the caller's session-header identity, ANDed with the other dimensions; unknown identity never matches (fail closed), so agent-scoped rules cannot leak onto unidentified callers.
|
|
27
|
+
- Path normalization: a candidate equal to the workspace root itself is dropped explicitly, and the root comparison only ignores case when `caseInsensitivePaths` is on.
|
|
28
|
+
|
|
29
|
+
### Dry-run rollout and audit fidelity
|
|
30
|
+
|
|
31
|
+
- `enforce: false` dry-run mode: deny/ask hits are audit-logged with a `dryRun` marker — the record keeps the would-be action AND the real downstream outcome — while every call is delegated via `next()`. `/rules` prints a dry-run notice while the mode is active and `/rules decisions` renders `(dry-run → <outcome>)` on such rows. Trial a new policy in production before enforcing it.
|
|
32
|
+
- `permissionRules/decision` events now carry `cwd`, the workspace the rule chain was resolved for.
|
|
33
|
+
|
|
34
|
+
### Commands and observability
|
|
35
|
+
|
|
36
|
+
- `/rules test` accepts leading flags: `--cwd <dir>` (evaluate against another workspace's rules), `--env KEY=VALUE` (repeatable; overrides host env for `when.env`), and `--agent <selector>` (repeatable; supplies identity candidates for the `agents` dimension). Quoted JSON argument tails are preserved verbatim.
|
|
37
|
+
- `resolveConfig` validates the closed enums (`badFilePolicy`, `patternMode`, `language`, `audit`) and boolean flags loudly, so plain-JS mounts without the Schemastery loader also fail on bad values instead of crashing later.
|
|
38
|
+
|
|
39
|
+
### Engineering
|
|
40
|
+
|
|
41
|
+
- 125 tests across 8 suites; coverage gate held. Five-language READMEs, both rules-format references, the JSON Schema, and the `/rules` command hint updated for `agents` and `enforce`.
|
|
42
|
+
|
|
5
43
|
## v0.2.0 — 2026-08-14
|
|
6
44
|
|
|
7
45
|
### Rules vocabulary
|
package/README.es.md
CHANGED
|
@@ -48,14 +48,15 @@ Un segundo modelo responde *"¿es segura ESTA llamada?"* con criterio, pero cues
|
|
|
48
48
|
## Características
|
|
49
49
|
|
|
50
50
|
- ✅ **Semántica de tres estados** — `allow`, `deny`, `ask`, evaluadas en orden de archivo; gana la primera coincidencia
|
|
51
|
-
- ✅ **Coincidencia rica** — globs de nombre de herramienta (incluido `mcp__*`), globs **o** regex de clave/valor de argumentos (con negación `!pattern` y la dimensión de clave `absent`), globs de rutas relativas al workspace extraídos de claves de argumentos documentadas a **cualquier profundidad de anidamiento**, y condiciones de host `when` (variables de entorno, plataforma)
|
|
51
|
+
- ✅ **Coincidencia rica** — globs de nombre de herramienta (incluido `mcp__*`), selectores de identidad de agente (`main` / `subagent` / `preset:*`), globs **o** regex de clave/valor de argumentos (con negación `!pattern` y la dimensión de clave `absent`), globs de rutas relativas al workspace extraídos de claves de argumentos documentadas a **cualquier profundidad de anidamiento**, y condiciones de host `when` (variables de entorno, plataforma)
|
|
52
52
|
- ✅ **Archivos de reglas jerárquicos** — `searchUp` opcional combina cada `.dsh/rules.yaml` desde el cwd de la sesión hasta la raíz del sistema de archivos, el más cercano primero, para que un proyecto hijo pueda sobrescribir las reglas del padre
|
|
53
53
|
- ✅ **Metadatos de reglas** — `enabled: false`, `description`, `tags`; `/rules` advierte sobre reglas eclipsadas por un catch-all anterior
|
|
54
54
|
- ✅ **Seguro para el waterfall** — `allow`/paso siempre llaman a `next()`; solo `deny`/`ask` cortocircuitan
|
|
55
55
|
- ✅ **Seam de aprobación oficial** — `ask` fluye por `ctx.approval`; nunca reimplementado, nunca eludido
|
|
56
|
-
- ✅ **Auditoría completa** — los eventos `permissionRules/decision` llevan la acción de la regla Y el resultado final de cada llamada; `/rules decisions` reproduce el rastro en la sesión
|
|
57
|
-
- ✅ **
|
|
58
|
-
- ✅ **
|
|
56
|
+
- ✅ **Auditoría completa** — los eventos `permissionRules/decision` llevan la acción de la regla, el cwd del workspace Y el resultado final de cada llamada; `/rules decisions` reproduce el rastro en la sesión
|
|
57
|
+
- ✅ **Despliegue en simulación** — `enforce: false` audita lo que la política *haría* (acción hipotética + resultado real posterior, marcado `dryRun`) mientras deja pasar todas las llamadas; prueba segura de políticas en producción
|
|
58
|
+
- ✅ **Prueba en seco** — `/rules test <tool> <json-args>` evalúa las reglas activas sin ejecutar nada, con sobrescrituras `--cwd`, `--env`, `--agent` y `--platform` para cada dimensión
|
|
59
|
+
- ✅ **Recarga en caliente** — vigilancia Chokidar con debounce; una edición rota conserva las reglas previas, nunca falla; un archivo de reglas creado a mitad de sesión (el del proyecto o el fallback) se adopta automáticamente, sin recarga manual
|
|
59
60
|
- ✅ **Fallo ruidoso** — YAML inválido, acciones/campos desconocidos, globs/regex incorrectos, patrones propensos a backtracking o > `maxRules` hacen fallar la carga
|
|
60
61
|
- ✅ **Ruta caliente acotada** — matchers precompilados, O(reglas × patrones), limitado por `maxRules`; el grado de backtracking de glob limitado por `maxGlobStars`
|
|
61
62
|
|
|
@@ -67,7 +68,7 @@ dsh plugin --profile web add "github:PerryLink/dsh-permission-rules#main"
|
|
|
67
68
|
|
|
68
69
|
# o desde un tarball empaquetado (artefactos compilados, sin permiso de build)
|
|
69
70
|
pnpm pack
|
|
70
|
-
dsh plugin --profile web add ./dsh-permission-rules-0.
|
|
71
|
+
dsh plugin --profile web add ./dsh-permission-rules-0.4.0.tgz
|
|
71
72
|
|
|
72
73
|
# 2. reinicia
|
|
73
74
|
dsh --profile web
|
|
@@ -112,16 +113,20 @@ Todos los ajustes son campos `Config` de Schemastery (modificables desde cordis.
|
|
|
112
113
|
| `audit` | `all` | Granularidad de auditoría: `all` registra cada acierto Y cada paso; `hits` omite los eventos de paso |
|
|
113
114
|
| `searchUp` | `false` | Recorre los directorios padre desde el cwd de la sesión y combina cada archivo de reglas encontrado, el más cercano primero |
|
|
114
115
|
| `maxGlobStars` | `2` | Límite duro de cuantificadores `*`/`**` sin límite por patrón glob (cota del grado de backtracking) |
|
|
116
|
+
| `enforce` | `true` | `false` = modo simulación: los aciertos deny/ask solo se registran en auditoría con un marcador `dryRun` (acción hipotética + resultado real posterior) y todas las llamadas pasan — prueba una política antes de aplicarla |
|
|
115
117
|
|
|
116
118
|
### Comandos de sesión
|
|
117
119
|
|
|
118
120
|
```
|
|
119
121
|
/rules lista las reglas activas, sus archivos fuente y cualquier error de la última recarga
|
|
122
|
+
/rules list alias explícito del listado simple
|
|
120
123
|
/rules reload relee la cadena de archivos de reglas de este workspace
|
|
121
124
|
/rules decisions [n] muestra las últimas n decisiones de permisos de esta sesión (por defecto 10)
|
|
122
125
|
/rules test <tool> <json> evalúa en seco las reglas contra una llamada hipotética, p. ej. /rules test bash {"command":"git push origin main"}
|
|
123
126
|
```
|
|
124
127
|
|
|
128
|
+
`/rules test` también acepta banderas iniciales: `--cwd <dir>` evalúa contra otro workspace, `--env CLAVE=VALOR` (repetible) sobrescribe el entorno para `when.env`, `--agent <selector>` (repetible) suministra candidatos de identidad para la dimensión `agents`, y `--platform <nombre>` sobrescribe la plataforma para `when.platform`. En cadenas de varios archivos (p. ej. `searchUp`), cada línea de regla se atribuye a su propio archivo fuente.
|
|
129
|
+
|
|
125
130
|
La salida de los comandos es solo UI: el modelo aprende las reglas únicamente a través de los resultados de herramienta que producen. `language` elige el idioma de salida. Un JSON Schema del archivo de reglas se distribuye en [docs/rules-format.schema.json](docs/rules-format.schema.json) (conéctalo con `# yaml-language-server: $schema=...` para autocompletado en el editor).
|
|
126
131
|
|
|
127
132
|
## Colaboración con dsh-auto-review
|
|
@@ -168,7 +173,7 @@ node scripts/repair-session-logs.mjs repair [--home DIR] [--dry-run]
|
|
|
168
173
|
pnpm install # node ^22.19 || >=24
|
|
169
174
|
pnpm run typecheck # tsc, src + tests
|
|
170
175
|
pnpm run lint # eslint, src + tests + scripts
|
|
171
|
-
pnpm test # vitest:
|
|
176
|
+
pnpm test # vitest: 133 tests, 8 suites
|
|
172
177
|
pnpm run test:coverage # puerta de cobertura (90/80/90/90)
|
|
173
178
|
pnpm run build # declaraciones tsc + bundles tsdown (lib/)
|
|
174
179
|
pnpm run pack:check # build + pack (el artefacto publicado)
|
package/README.hi.md
CHANGED
|
@@ -48,14 +48,15 @@ tools/pre-execute waterfall approval/request waterfall (answ
|
|
|
48
48
|
## विशेषताएँ
|
|
49
49
|
|
|
50
50
|
- ✅ **तीन-अवस्था शब्दार्थ** — `allow`, `deny`, `ask`, फ़ाइल क्रम में मूल्यांकित; पहला मिलान जीतता है
|
|
51
|
-
- ✅ **समृद्ध मिलान** — टूल-नाम globs (`mcp__*` सहित), आर्गुमेंट कुंजी/मान globs **या** regex (`!pattern` निषेध और `absent` कुंजी आयाम के साथ), दस्तावेज़ित आर्गुमेंट कुंजियों से **किसी भी नेस्टिंग गहराई** पर निकाले गए workspace-सापेक्ष पथ globs, और `when` होस्ट शर्तें (env vars, प्लेटफ़ॉर्म)
|
|
51
|
+
- ✅ **समृद्ध मिलान** — टूल-नाम globs (`mcp__*` सहित), एजेंट-पहचान चयनकर्ता (`main` / `subagent` / `preset:*`), आर्गुमेंट कुंजी/मान globs **या** regex (`!pattern` निषेध और `absent` कुंजी आयाम के साथ), दस्तावेज़ित आर्गुमेंट कुंजियों से **किसी भी नेस्टिंग गहराई** पर निकाले गए workspace-सापेक्ष पथ globs, और `when` होस्ट शर्तें (env vars, प्लेटफ़ॉर्म)
|
|
52
52
|
- ✅ **पदानुक्रमित नियम फ़ाइलें** — वैकल्पिक `searchUp` सत्र cwd से फ़ाइलसिस्टम रूट तक हर `.dsh/rules.yaml` को मिलाता है, निकटतम पहले, ताकि चाइल्ड प्रोजेक्ट पैरेंट नियमों को ओवरराइड कर सके
|
|
53
53
|
- ✅ **नियम मेटाडेटा** — `enabled: false`, `description`, `tags`; `/rules` पहले किसी catch-all से छिपे नियमों की चेतावनी देता है
|
|
54
54
|
- ✅ **waterfall-सुरक्षित** — `allow`/पास-थ्रू हमेशा `next()` बुलाते हैं; केवल `deny`/`ask` शॉर्ट-सर्किट करते हैं
|
|
55
55
|
- ✅ **आधिकारिक approval seam** — `ask` `ctx.approval` से होकर जाता है; कभी पुनः-कार्यान्वित नहीं, कभी बायपास नहीं
|
|
56
|
-
- ✅ **पूर्ण ऑडिट** — `permissionRules/decision` इवेंट हर कॉल के लिए नियम
|
|
57
|
-
- ✅ **dry-run
|
|
58
|
-
- ✅
|
|
56
|
+
- ✅ **पूर्ण ऑडिट** — `permissionRules/decision` इवेंट हर कॉल के लिए नियम क्रिया, workspace cwd और अंतिम परिणाम रखते हैं; `/rules decisions` सत्र में रास्ता फिर चलाता है
|
|
57
|
+
- ✅ **dry-run रोलआउट** — `enforce: false` केवल ऑडिट करता है कि नीति *क्या करती* (काल्पनिक क्रिया + वास्तविक डाउनस्ट्रीम परिणाम, `dryRun` चिह्नित) और हर कॉल को पास करता है; उत्पादन में नई नीति का सुरक्षित परीक्षण
|
|
58
|
+
- ✅ **dry-run परीक्षण** — `/rules test <tool> <json-args>` कुछ भी निष्पादित किए बिना सक्रिय नियमों का मूल्यांकन करता है, हर मिलान आयाम के लिए `--cwd`, `--env`, `--agent` और `--platform` ओवरराइड के साथ
|
|
59
|
+
- ✅ **हॉट रीलोड** — debounce सहित Chokidar निगरानी; टूटा हुआ संपादन पिछले नियम रखता है, कभी क्रैश नहीं करता; सत्र के बीच बनाई गई नियम फ़ाइल (प्रोजेक्ट की या fallback) स्वतः अपनाई जाती है, मैनुअल रीलोड की आवश्यकता नहीं
|
|
59
60
|
- ✅ **ज़ोरदार विफलता** — अमान्य YAML, अज्ञात action/फ़ील्ड, ख़राब globs/regex, बैकट्रैकिंग-प्रवण पैटर्न, या `maxRules` से अधिक → लोड विफल
|
|
60
61
|
- ✅ **सीमित हॉट पथ** — पूर्व-संकलित matchers, O(नियम × पैटर्न), `maxRules` द्वारा सीमित; glob बैकट्रैकिंग डिग्री `maxGlobStars` द्वारा सीमित
|
|
61
62
|
|
|
@@ -67,7 +68,7 @@ dsh plugin --profile web add "github:PerryLink/dsh-permission-rules#main"
|
|
|
67
68
|
|
|
68
69
|
# या पैक किए गए tarball से (बिल्ट आर्टिफ़ैक्ट, build अनुमति की ज़रूरत नहीं)
|
|
69
70
|
pnpm pack
|
|
70
|
-
dsh plugin --profile web add ./dsh-permission-rules-0.
|
|
71
|
+
dsh plugin --profile web add ./dsh-permission-rules-0.4.0.tgz
|
|
71
72
|
|
|
72
73
|
# 2. पुनः आरंभ करें
|
|
73
74
|
dsh --profile web
|
|
@@ -112,16 +113,20 @@ dsh --profile web --dump-config | grep -A4 'id: permission-rules' # पंक
|
|
|
112
113
|
| `audit` | `all` | ऑडिट विवरण स्तर: `all` हर मिलान और पास-थ्रू लॉग करता है; `hits` पास-थ्रू इवेंट छोड़ता है |
|
|
113
114
|
| `searchUp` | `false` | सत्र cwd से पैरेंट निर्देशिकाओं पर चलकर हर मिली नियम फ़ाइल मिलाता है, निकटतम पहले |
|
|
114
115
|
| `maxGlobStars` | `2` | प्रति glob पैटर्न असीमित `*`/`**` क्वांटिफ़ायर की कठोर सीमा (बैकट्रैकिंग-डिग्री सीमा) |
|
|
116
|
+
| `enforce` | `true` | `false` = dry-run मोड: deny/ask मिलान केवल ऑडिट में `dryRun` चिह्न के साथ दर्ज होते हैं (काल्पनिक क्रिया + वास्तविक डाउनस्ट्रीम परिणाम) और हर कॉल पास होती है — लागू करने से पहले नीति का परीक्षण करें |
|
|
115
117
|
|
|
116
118
|
### सेशन कमांड
|
|
117
119
|
|
|
118
120
|
```
|
|
119
121
|
/rules सक्रिय नियम, उनकी स्रोत फ़ाइलें और अंतिम रीलोड त्रुटि दिखाता है
|
|
122
|
+
/rules list सामान्य सूची का स्पष्ट उपनाम
|
|
120
123
|
/rules reload इस workspace की नियम-फ़ाइल शृंखला दोबारा पढ़ता है
|
|
121
124
|
/rules decisions [n] इस सत्र की अंतिम n अनुमति निर्णय दिखाता है (डिफ़ॉल्ट 10)
|
|
122
125
|
/rules test <tool> <json> काल्पनिक कॉल के विरुद्ध नियमों का dry-मूल्यांकन, जैसे /rules test bash {"command":"git push origin main"}
|
|
123
126
|
```
|
|
124
127
|
|
|
128
|
+
`/rules test` शुरुआती फ़्लैग भी स्वीकार करता है: `--cwd <dir>` दूसरे workspace के विरुद्ध मूल्यांकन करता है, `--env KEY=मान` (दोहराने योग्य) `when.env` के लिए होस्ट env ओवरराइड करता है, `--agent <चयनकर्ता>` (दोहराने योग्य) `agents` आयाम के लिए पहचान उम्मीदवार देता है, और `--platform <नाम>` `when.platform` के लिए होस्ट प्लेटफ़ॉर्म ओवरराइड करता है। बहु-फ़ाइल शृंखलाओं (जैसे `searchUp`) में हर नियम पंक्ति अपनी स्रोत फ़ाइल के साथ चिह्नित होती है।
|
|
129
|
+
|
|
125
130
|
कमांड आउटपुट केवल UI के लिए है — मॉडल नियमों को केवल उनके उत्पन्न टूल परिणामों से ही सीखता है। `language` आउटपुट भाषा चुनता है। नियम फ़ाइल का JSON Schema [docs/rules-format.schema.json](docs/rules-format.schema.json) पर साथ आता है (संपादक पूर्णता के लिए `# yaml-language-server: $schema=...` से जोड़ें)।
|
|
126
131
|
|
|
127
132
|
## dsh-auto-review के साथ सहयोग
|
|
@@ -168,7 +173,7 @@ node scripts/repair-session-logs.mjs repair [--home DIR] [--dry-run]
|
|
|
168
173
|
pnpm install # node ^22.19 || >=24
|
|
169
174
|
pnpm run typecheck # tsc, src + tests
|
|
170
175
|
pnpm run lint # eslint, src + tests + scripts
|
|
171
|
-
pnpm test # vitest:
|
|
176
|
+
pnpm test # vitest: 133 tests, 8 suites
|
|
172
177
|
pnpm run test:coverage # कवरेज द्वार (90/80/90/90)
|
|
173
178
|
pnpm run build # tsc डिक्लेरेशन + tsdown bundles (lib/)
|
|
174
179
|
pnpm run pack:check # build + pack (प्रकाशित आर्टिफ़ैक्ट)
|
package/README.md
CHANGED
|
@@ -48,14 +48,15 @@ A second model answers *"is THIS call okay?"* with judgment, but costs a round-t
|
|
|
48
48
|
## Features
|
|
49
49
|
|
|
50
50
|
- ✅ **Three-state semantics** — `allow`, `deny`, `ask`, evaluated in file order, first match wins
|
|
51
|
-
- ✅ **Rich matching** — tool-name globs (including `mcp__*`), argument key/value globs **or** regexes (with `!pattern` negation and an `absent` key dimension), workspace-relative path globs extracted from documented argument keys at **any nesting depth**, and `when` host conditions (env vars, platform)
|
|
51
|
+
- ✅ **Rich matching** — tool-name globs (including `mcp__*`), agent-identity selectors (`main` / `subagent` / `preset:*`), argument key/value globs **or** regexes (with `!pattern` negation and an `absent` key dimension), workspace-relative path globs extracted from documented argument keys at **any nesting depth**, and `when` host conditions (env vars, platform)
|
|
52
52
|
- ✅ **Hierarchical rule files** — optional `searchUp` merges every `.dsh/rules.yaml` from the session cwd to the filesystem root, nearest first, so a child project can override parent rules
|
|
53
53
|
- ✅ **Rule metadata** — `enabled: false`, `description`, `tags`; `/rules` warns about rules shadowed by an earlier catch-all
|
|
54
54
|
- ✅ **Waterfall-safe** — `allow`/passthrough always call `next()`; only `deny`/`ask` short-circuit
|
|
55
55
|
- ✅ **Official approval seam** — `ask` flows through `ctx.approval`; never re-implemented, never bypassed
|
|
56
|
-
- ✅ **Full audit** — `permissionRules/decision` events carry the rule action AND the final outcome for every call; `/rules decisions` replays the trail in-session
|
|
57
|
-
- ✅ **Dry-run
|
|
58
|
-
- ✅ **
|
|
56
|
+
- ✅ **Full audit** — `permissionRules/decision` events carry the rule action, the workspace cwd, AND the final outcome for every call; `/rules decisions` replays the trail in-session
|
|
57
|
+
- ✅ **Dry-run rollout** — `enforce: false` audits what the policy *would* do (would-be action + real downstream outcome, `dryRun`-marked) while passing every call through; safe policy trialing in production
|
|
58
|
+
- ✅ **Dry-run testing** — `/rules test <tool> <json-args>` evaluates the active rules without executing anything, with `--cwd`, `--env`, `--agent`, and `--platform` overrides for every match dimension
|
|
59
|
+
- ✅ **Hot reload** — Chokidar watch with debounce; a broken edit keeps the previous rules, never crashes; a rule file created mid-session (the project file or the fallback) is adopted automatically, no manual reload
|
|
59
60
|
- ✅ **Fail loud** — invalid YAML, unknown actions/fields, bad globs/regexes, backtracking-prone patterns, or > `maxRules` fail the load
|
|
60
61
|
- ✅ **Bounded hot path** — precompiled matchers, O(rules × patterns), capped by `maxRules`; glob backtracking degree capped by `maxGlobStars`
|
|
61
62
|
|
|
@@ -67,7 +68,7 @@ dsh plugin --profile web add "github:PerryLink/dsh-permission-rules#main"
|
|
|
67
68
|
|
|
68
69
|
# or from a packed tarball (built artifacts, no build permission needed)
|
|
69
70
|
pnpm pack
|
|
70
|
-
dsh plugin --profile web add ./dsh-permission-rules-0.
|
|
71
|
+
dsh plugin --profile web add ./dsh-permission-rules-0.4.0.tgz
|
|
71
72
|
|
|
72
73
|
# 2. restart
|
|
73
74
|
dsh --profile web
|
|
@@ -112,16 +113,20 @@ All tunables are Schemastery `Config` fields (changeable from cordis.yml). An id
|
|
|
112
113
|
| `audit` | `all` | Audit granularity: `all` logs every hit AND passthrough; `hits` skips passthrough events |
|
|
113
114
|
| `searchUp` | `false` | Walk parent directories from the session cwd and merge every found rule file, nearest first |
|
|
114
115
|
| `maxGlobStars` | `2` | Hard cap on unbounded `*`/`**` quantifiers per glob pattern (backtracking-degree bound) |
|
|
116
|
+
| `enforce` | `true` | `false` = dry-run mode: deny/ask hits are audit-logged with a `dryRun` marker (would-be action + real downstream outcome) and every call passes through — trial a policy before enforcing it |
|
|
115
117
|
|
|
116
118
|
### Session commands
|
|
117
119
|
|
|
118
120
|
```
|
|
119
121
|
/rules list the active rules, their source files, and any last-reload error
|
|
122
|
+
/rules list explicit alias for the bare listing
|
|
120
123
|
/rules reload re-read the rule-file chain for this workspace
|
|
121
124
|
/rules decisions [n] show the last n permission decisions of this session (default 10)
|
|
122
125
|
/rules test <tool> <json> dry-evaluate the rules against a hypothetical call, e.g. /rules test bash {"command":"git push origin main"}
|
|
123
126
|
```
|
|
124
127
|
|
|
128
|
+
`/rules test` also accepts leading flags: `--cwd <dir>` evaluates against another workspace, `--env KEY=VALUE` (repeatable) overrides host env for `when.env`, `--agent <selector>` (repeatable) supplies identity candidates for the `agents` dimension, and `--platform <name>` overrides the host platform for `when.platform`. In multi-file chains (e.g. `searchUp`), every listed rule line is attributed to its own source file.
|
|
129
|
+
|
|
125
130
|
Command output is UI-only — the model learns the rules only through the tool results they produce. `language` picks the output language. A JSON Schema for the rule file ships at [docs/rules-format.schema.json](docs/rules-format.schema.json) (wire it up with `# yaml-language-server: $schema=...` for editor completion).
|
|
126
131
|
|
|
127
132
|
## Collaborating with dsh-auto-review
|
|
@@ -168,7 +173,7 @@ node scripts/repair-session-logs.mjs repair [--home DIR] [--dry-run]
|
|
|
168
173
|
pnpm install # node ^22.19 || >=24
|
|
169
174
|
pnpm run typecheck # tsc, src + tests
|
|
170
175
|
pnpm run lint # eslint, src + tests + scripts
|
|
171
|
-
pnpm test # vitest:
|
|
176
|
+
pnpm test # vitest: 133 tests, 8 suites
|
|
172
177
|
pnpm run test:coverage # coverage gate (90/80/90/90)
|
|
173
178
|
pnpm run build # tsc declarations + tsdown bundles (lib/)
|
|
174
179
|
pnpm run pack:check # build + pack (the published artifact)
|
package/README.pt.md
CHANGED
|
@@ -48,14 +48,15 @@ Um segundo modelo responde *"ESTA chamada é segura?"* com julgamento, mas custa
|
|
|
48
48
|
## Funcionalidades
|
|
49
49
|
|
|
50
50
|
- ✅ **Semântica de três estados** — `allow`, `deny`, `ask`, avaliadas na ordem do arquivo; a primeira correspondência vence
|
|
51
|
-
- ✅ **Correspondência rica** — globs de nome de ferramenta (incluindo `mcp__*`), globs **ou** regex de chave/valor de argumentos (com negação `!pattern` e a dimensão de chave `absent`), globs de caminhos relativos ao workspace extraídos de chaves de argumentos documentadas em **qualquer profundidade de aninhamento**, e condições de host `when` (variáveis de ambiente, plataforma)
|
|
51
|
+
- ✅ **Correspondência rica** — globs de nome de ferramenta (incluindo `mcp__*`), seletores de identidade de agente (`main` / `subagent` / `preset:*`), globs **ou** regex de chave/valor de argumentos (com negação `!pattern` e a dimensão de chave `absent`), globs de caminhos relativos ao workspace extraídos de chaves de argumentos documentadas em **qualquer profundidade de aninhamento**, e condições de host `when` (variáveis de ambiente, plataforma)
|
|
52
52
|
- ✅ **Arquivos de regras hierárquicos** — `searchUp` opcional combina cada `.dsh/rules.yaml` do cwd da sessão até a raiz do sistema de arquivos, o mais próximo primeiro, para que um projeto filho possa sobrescrever regras do pai
|
|
53
53
|
- ✅ **Metadados de regras** — `enabled: false`, `description`, `tags`; `/rules` avisa sobre regras encobertas por um catch-all anterior
|
|
54
54
|
- ✅ **Seguro para o waterfall** — `allow`/passagem sempre chamam `next()`; somente `deny`/`ask` curto-circuitam
|
|
55
55
|
- ✅ **Seam de aprovação oficial** — `ask` flui por `ctx.approval`; nunca reimplementado, nunca contornado
|
|
56
|
-
- ✅ **Auditoria completa** — os eventos `permissionRules/decision` carregam a ação da regra E o resultado final de cada chamada; `/rules decisions` reproduz o rastro na sessão
|
|
57
|
-
- ✅ **
|
|
58
|
-
- ✅ **
|
|
56
|
+
- ✅ **Auditoria completa** — os eventos `permissionRules/decision` carregam a ação da regra, o cwd do workspace E o resultado final de cada chamada; `/rules decisions` reproduz o rastro na sessão
|
|
57
|
+
- ✅ **Implantação em simulação** — `enforce: false` audita o que a política *faria* (ação hipotética + resultado real posterior, marcado com `dryRun`) enquanto deixa todas as chamadas passarem; teste seguro de políticas em produção
|
|
58
|
+
- ✅ **Teste em seco** — `/rules test <tool> <json-args>` avalia as regras ativas sem executar nada, com sobrescritas `--cwd`, `--env`, `--agent` e `--platform` para cada dimensão
|
|
59
|
+
- ✅ **Recarga a quente** — vigilância Chokidar com debounce; uma edição quebrada mantém as regras anteriores, nunca quebra; um arquivo de regras criado no meio da sessão (o do projeto ou o fallback) é adotado automaticamente, sem recarga manual
|
|
59
60
|
- ✅ **Falha ruidosa** — YAML inválido, ações/campos desconhecidos, globs/regex ruins, padrões propensos a backtracking ou > `maxRules` fazem a carga falhar
|
|
60
61
|
- ✅ **Caminho quente limitado** — matchers pré-compilados, O(regras × padrões), limitado por `maxRules`; o grau de backtracking de glob limitado por `maxGlobStars`
|
|
61
62
|
|
|
@@ -67,7 +68,7 @@ dsh plugin --profile web add "github:PerryLink/dsh-permission-rules#main"
|
|
|
67
68
|
|
|
68
69
|
# ou a partir de um tarball empacotado (artefatos compilados, sem permissão de build)
|
|
69
70
|
pnpm pack
|
|
70
|
-
dsh plugin --profile web add ./dsh-permission-rules-0.
|
|
71
|
+
dsh plugin --profile web add ./dsh-permission-rules-0.4.0.tgz
|
|
71
72
|
|
|
72
73
|
# 2. reinicie
|
|
73
74
|
dsh --profile web
|
|
@@ -112,16 +113,20 @@ Todos os ajustes são campos `Config` do Schemastery (alteráveis no cordis.yml)
|
|
|
112
113
|
| `audit` | `all` | Granularidade da auditoria: `all` registra cada acerto E passagem; `hits` pula eventos de passagem |
|
|
113
114
|
| `searchUp` | `false` | Percorre os diretórios pais a partir do cwd da sessão e combina cada arquivo de regras encontrado, o mais próximo primeiro |
|
|
114
115
|
| `maxGlobStars` | `2` | Limite rígido de quantificadores `*`/`**` ilimitados por padrão glob (cota do grau de backtracking) |
|
|
116
|
+
| `enforce` | `true` | `false` = modo simulação: acertos deny/ask são apenas registrados na auditoria com um marcador `dryRun` (ação hipotética + resultado real posterior) e todas as chamadas passam — teste uma política antes de aplicá-la |
|
|
115
117
|
|
|
116
118
|
### Comandos de sessão
|
|
117
119
|
|
|
118
120
|
```
|
|
119
121
|
/rules lista as regras ativas, seus arquivos de origem e qualquer erro da última recarga
|
|
122
|
+
/rules list alias explícito da listagem simples
|
|
120
123
|
/rules reload relê a cadeia de arquivos de regras deste workspace
|
|
121
124
|
/rules decisions [n] mostra as últimas n decisões de permissão desta sessão (padrão 10)
|
|
122
125
|
/rules test <tool> <json> avalia em seco as regras contra uma chamada hipotética, ex.: /rules test bash {"command":"git push origin main"}
|
|
123
126
|
```
|
|
124
127
|
|
|
128
|
+
`/rules test` também aceita sinalizadores iniciais: `--cwd <dir>` avalia contra outro workspace, `--env CHAVE=VALOR` (repetível) sobrescreve o ambiente para `when.env`, `--agent <seletor>` (repetível) fornece candidatos de identidade para a dimensão `agents`, e `--platform <nome>` sobrescreve a plataforma para `when.platform`. Em cadeias de vários arquivos (ex.: `searchUp`), cada linha de regra é atribuída ao seu próprio arquivo de origem.
|
|
129
|
+
|
|
125
130
|
A saída dos comandos é somente UI — o modelo aprende as regras apenas pelos resultados de ferramenta que elas produzem. `language` escolhe o idioma da saída. Um JSON Schema do arquivo de regras é distribuído em [docs/rules-format.schema.json](docs/rules-format.schema.json) (conecte-o com `# yaml-language-server: $schema=...` para autocompletar no editor).
|
|
126
131
|
|
|
127
132
|
## Colaboração com o dsh-auto-review
|
|
@@ -168,7 +173,7 @@ node scripts/repair-session-logs.mjs repair [--home DIR] [--dry-run]
|
|
|
168
173
|
pnpm install # node ^22.19 || >=24
|
|
169
174
|
pnpm run typecheck # tsc, src + tests
|
|
170
175
|
pnpm run lint # eslint, src + tests + scripts
|
|
171
|
-
pnpm test # vitest:
|
|
176
|
+
pnpm test # vitest: 133 tests, 8 suites
|
|
172
177
|
pnpm run test:coverage # portão de cobertura (90/80/90/90)
|
|
173
178
|
pnpm run build # declarações tsc + bundles tsdown (lib/)
|
|
174
179
|
pnpm run pack:check # build + pack (o artefato publicado)
|
package/README.zh.md
CHANGED
|
@@ -48,14 +48,15 @@ tools/pre-execute waterfall approval/request waterfall(ans
|
|
|
48
48
|
## 特性
|
|
49
49
|
|
|
50
50
|
- ✅ **三态语义** — `allow`、`deny`、`ask`,按文件顺序求值,首条命中生效
|
|
51
|
-
- ✅ **丰富匹配** — 工具名 glob(含 `mcp__
|
|
51
|
+
- ✅ **丰富匹配** — 工具名 glob(含 `mcp__*`)、代理身份选择器(`main` / `subagent` / `preset:*`)、参数键值 glob **或** 正则(支持 `!pattern` 否定与 `absent` 键维度)、工作区相对路径 glob(从文档化参数键在**任意嵌套深度**提取候选),以及 `when` 宿主条件(环境变量、平台)
|
|
52
52
|
- ✅ **分层规则文件** — 可选 `searchUp` 从会话 cwd 向上逐级合并每份 `.dsh/rules.yaml`(最近的优先),子项目可覆盖父级规则
|
|
53
53
|
- ✅ **规则元数据** — `enabled: false`、`description`、`tags`;`/rules` 会告警被前面通配规则遮蔽的规则
|
|
54
54
|
- ✅ **waterfall 安全** — `allow`/透传一律 `next()`;只有 `deny`/`ask` 短路
|
|
55
55
|
- ✅ **官方审批 seam** — `ask` 走 `ctx.approval`;不重复实现、绝不绕过
|
|
56
|
-
- ✅ **完整审计** — `permissionRules/decision`
|
|
57
|
-
- ✅
|
|
58
|
-
- ✅
|
|
56
|
+
- ✅ **完整审计** — `permissionRules/decision` 事件记录规则动作、工作区 cwd 与最终裁决;`/rules decisions` 可在会话内回放审计轨迹
|
|
57
|
+
- ✅ **干跑上线** — `enforce: false` 只审计策略「本会做什么」(记录本会动作 + 下游真实结果,带 `dryRun` 标记),所有调用照常透传;可在生产环境安全试跑新策略
|
|
58
|
+
- ✅ **干跑测试** — `/rules test <工具> <json-参数>` 不执行任何东西地评估规则命中,支持 `--cwd`、`--env`、`--agent`、`--platform` 覆盖每个匹配维度
|
|
59
|
+
- ✅ **热更新** — Chokidar 监视 + 去抖;改坏了保留旧规则,绝不崩溃;会话中途创建的规则文件(项目文件或 fallback)自动生效,无需手动重载
|
|
59
60
|
- ✅ **响亮失败** — 非法 YAML、未知字段/action、坏 glob/正则、易回溯灾难的模式、超过 `maxRules` 都使加载失败
|
|
60
61
|
- ✅ **热路径有界** — 预编译匹配器,O(规则数 × 模式数),`maxRules` 封顶;glob 回溯度由 `maxGlobStars` 封顶
|
|
61
62
|
|
|
@@ -67,7 +68,7 @@ dsh plugin --profile web add "github:PerryLink/dsh-permission-rules#main"
|
|
|
67
68
|
|
|
68
69
|
# 或从打包好的 tarball 安装(预构建产物,无需构建许可)
|
|
69
70
|
pnpm pack
|
|
70
|
-
dsh plugin --profile web add ./dsh-permission-rules-0.
|
|
71
|
+
dsh plugin --profile web add ./dsh-permission-rules-0.4.0.tgz
|
|
71
72
|
|
|
72
73
|
# 2. 重启
|
|
73
74
|
dsh --profile web
|
|
@@ -112,16 +113,20 @@ dsh --profile web --dump-config | grep -A4 'id: permission-rules' # 验证挂
|
|
|
112
113
|
| `audit` | `all` | 审计粒度:`all` 记录每次命中与透传;`hits` 跳过透传事件 |
|
|
113
114
|
| `searchUp` | `false` | 从会话 cwd 向上逐级查找并合并规则文件,最近的优先 |
|
|
114
115
|
| `maxGlobStars` | `2` | 每个 glob 模式的无界 `*`/`**` 量词数硬上界(回溯度封顶) |
|
|
116
|
+
| `enforce` | `true` | `false` = 干跑模式:deny/ask 命中只写审计(带 `dryRun` 标记,记录本会动作与下游真实结果),所有调用照常透传——先试跑策略再强制执行 |
|
|
115
117
|
|
|
116
118
|
### 会话命令
|
|
117
119
|
|
|
118
120
|
```
|
|
119
121
|
/rules 列出当前生效规则、来源文件与最近一次重载错误
|
|
122
|
+
/rules list 裸列出的显式别名
|
|
120
123
|
/rules reload 重读本工作区的规则文件链
|
|
121
124
|
/rules decisions [n] 显示本会话最近 n 条权限裁决(默认 10)
|
|
122
125
|
/rules test <工具> <json> 对假设调用干跑评估,如 /rules test bash {"command":"git push origin main"}
|
|
123
126
|
```
|
|
124
127
|
|
|
128
|
+
`/rules test` 还支持前置标志:`--cwd <目录>` 换一个工作区评估,`--env 键=值`(可重复)覆盖宿主环境变量以测试 `when.env`,`--agent <选择器>`(可重复)为 `agents` 维度提供身份候选,`--platform <平台名>` 覆盖宿主平台以测试 `when.platform`。多文件链(如 `searchUp`)下,每条规则行都会标注它自己的来源文件。
|
|
129
|
+
|
|
125
130
|
命令输出只进 UI——模型只通过规则产生的工具结果感知规则。`language` 选择输出语言。规则文件的 JSON Schema 随包发布在 [docs/rules-format.schema.json](docs/rules-format.schema.json)(用 `# yaml-language-server: $schema=...` 启用编辑器补全)。
|
|
126
131
|
|
|
127
132
|
## 与 dsh-auto-review 协作
|
|
@@ -168,7 +173,7 @@ node scripts/repair-session-logs.mjs repair [--home DIR] [--dry-run]
|
|
|
168
173
|
pnpm install # node ^22.19 || >=24
|
|
169
174
|
pnpm run typecheck # tsc,src + tests
|
|
170
175
|
pnpm run lint # eslint,src + tests + scripts
|
|
171
|
-
pnpm test # vitest:
|
|
176
|
+
pnpm test # vitest:133 测试 / 8 套件
|
|
172
177
|
pnpm run test:coverage # 覆盖率门禁(90/80/90/90)
|
|
173
178
|
pnpm run build # tsc 声明 + tsdown 打包(lib/)
|
|
174
179
|
pnpm run pack:check # 构建 + pack(发布产物)
|
package/docs/rules-format.en.md
CHANGED
|
@@ -17,13 +17,14 @@ rules: # rule list, evaluated in written order; may be omitted or empty
|
|
|
17
17
|
reason: ...
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
-
Only `rules` is allowed at the top level; each rule allows only `match`, `action`, `reason`, `enabled`, `description`, `tags`; each `match` allows only `tools`, `params`, `paths`, `absent`, `when`. Any unknown field fails the load loudly — never silently ignored.
|
|
20
|
+
Only `rules` is allowed at the top level; each rule allows only `match`, `action`, `reason`, `enabled`, `description`, `tags`; each `match` allows only `tools`, `agents`, `params`, `paths`, `absent`, `when`. Any unknown field fails the load loudly — never silently ignored.
|
|
21
21
|
|
|
22
22
|
## match dimensions (AND)
|
|
23
23
|
|
|
24
24
|
| Dimension | Type | Semantics |
|
|
25
25
|
|---|---|---|
|
|
26
26
|
| `tools` | `string[]` | Tool-name globs (always globs, regardless of `patternMode`). Empty/absent = unrestricted. Supports prefixes like `mcp__*`. |
|
|
27
|
+
| `agents` | `string[]` | Agent-identity selector globs against the caller's session header candidates: `main` (top-level sessions), `subagent` (subagent children), and `preset:<name>` (when a preset composed the agent). **Any selector matching any candidate** satisfies the dimension; empty/absent = unrestricted. Unknown identity (no candidates) never matches, so agent-scoped rules fail closed. |
|
|
27
28
|
| `params` | `Record<string, string \| number \| boolean \| (…)[]>` | Argument key → pattern (or pattern list). **Every listed key must be present AND match** (AND); absent = unrestricted. Scalars match as strings (numbers/booleans stringified); array elements match any-of; nested objects contribute their scalar leaves (depth-capped at 8). A `!`-prefixed pattern negates: the value must NOT match it; a key with only negations matches when the key is present and no negation hits (quote `!` patterns in YAML: `"!git*"`). `/` is an ordinary character in params patterns — `*` crosses it. |
|
|
28
29
|
| `paths` | `string[]` | Workspace-relative path patterns; any candidate matching any pattern satisfies the dimension. Candidates come from argument values under these keys at ANY nesting depth (depth-capped): `path` `paths` `file` `files` `file_path` `dir` `directory` `directories` `cwd` `workspace` `root` `target` `targets` `output`. Absolute candidates outside the workspace are dropped; relative `../` forms are kept so explicit out-of-root globs still work. In path patterns `*` does not cross `/`; `**` crosses any depth (including zero). With `caseInsensitivePaths` on (Windows default) the root comparison and the patterns ignore ASCII case. |
|
|
29
30
|
| `absent` | `string[]` | Argument keys that must be ABSENT; **every listed key must be missing** (non-object arguments satisfy this trivially). |
|
|
@@ -99,7 +100,11 @@ This plugin only produces `ask` decisions; the `ask` goes to the official `ctx.a
|
|
|
99
100
|
|
|
100
101
|
## Loading and hot reload
|
|
101
102
|
|
|
102
|
-
- Lazily discovered per session cwd and cached on the first `tools/pre-execute` (least-recently-used eviction beyond `maxCachedWorkspaces`).
|
|
103
|
+
- Lazily discovered per session cwd and cached on the first `tools/pre-execute` (least-recently-used eviction beyond `maxCachedWorkspaces`); the cache key is the resolved cwd (case-folded on Windows), so differently-spelled paths share one entry.
|
|
103
104
|
- Invalid YAML, unknown fields/actions, bad globs/regexes, backtracking-prone patterns, and counts over `maxRules` are load-time errors: `badFilePolicy: fail` (default) makes the first tool call in that workspace fail loudly; `ignore-with-warning` warns and degrades to an empty rule set.
|
|
104
105
|
- An absolute `rulesFile` or a configured `fallbackPath` is validated at plugin mount — missing or invalid fails the mount.
|
|
105
106
|
- File changes reload through Chokidar with a debounce (`watch`, `watchStabilityThresholdMs`); a failed reload keeps the previous rules and only warns — never crashes. `/rules reload` triggers the same path manually; `/rules decisions` and `/rules test` inspect the trail and dry-run the rules.
|
|
107
|
+
- Expected-but-absent rule files (the project file when it is not in effect, a fallback after it was deleted) are watched through their deepest existing ancestor directory: creating one mid-session is adopted automatically without a manual reload. Under `searchUp` only the immediate cwd-level candidate is watched — creating a rule file in a deeper ancestor still needs `/rules reload`.
|
|
108
|
+
- `/rules` lists the active rules (with `list` as an explicit alias); in multi-file chains every rule line is attributed to its own source file.
|
|
109
|
+
- `/rules test` accepts leading flags: `--cwd <dir>` evaluates against another workspace (rule discovery AND path normalization), `--env KEY=VALUE` (repeatable) overrides host env for `when.env`, `--agent <selector>` (repeatable) supplies identity candidates for the `agents` dimension, and `--platform <name>` overrides the host platform for `when.platform`.
|
|
110
|
+
- `enforce: false` puts the plugin in dry-run mode: deny/ask hits are audit-logged with a `dryRun` marker (keeping the would-be action and the real downstream outcome) and every call passes through — use it to trial a new policy in production before enforcing it. `/rules` prints a dry-run notice while the mode is active.
|
package/docs/rules-format.md
CHANGED
|
@@ -17,13 +17,14 @@ rules: # 规则列表,按书写顺序求值;可以省略或为空
|
|
|
17
17
|
reason: ...
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
-
顶层只允许 `rules` 一个键;每条规则只允许 `match`、`action`、`reason`、`enabled`、`description`、`tags` 六个键;`match` 只允许 `tools`、`params`、`paths`、`absent`、`when`
|
|
20
|
+
顶层只允许 `rules` 一个键;每条规则只允许 `match`、`action`、`reason`、`enabled`、`description`、`tags` 六个键;`match` 只允许 `tools`、`agents`、`params`、`paths`、`absent`、`when` 六个键。出现任何未知键都在加载期报错(响亮失败),绝不静默忽略。
|
|
21
21
|
|
|
22
22
|
## match 维度(全部 AND)
|
|
23
23
|
|
|
24
24
|
| 维度 | 类型 | 语义 |
|
|
25
25
|
|---|---|---|
|
|
26
26
|
| `tools` | `string[]` | 工具名 glob(始终 glob 解释,与 `patternMode` 无关)。空/缺省 = 不限制。支持 `mcp__*` 等前缀匹配。 |
|
|
27
|
+
| `agents` | `string[]` | 调用方代理身份选择器 glob,匹配会话头推导出的候选:`main`(顶层会话)、`subagent`(子代理会话)、`preset:<name>`(由预设组合的代理)。**任一选择器命中任一候选**即满足该维度;空/缺省 = 不限制。身份未知(无候选)永不命中——面向代理的规则按失败关闭(fail closed)处理。 |
|
|
27
28
|
| `params` | `Record<string, string \| number \| boolean \| (…)[]>` | 参数键 → 模式(或模式列表)。**列出的每个键都必须存在且命中**(AND);缺省 = 不限制。标量值按字符串匹配(数字/布尔字符串化);数组任一元素命中即可;嵌套对象贡献其标量叶子(深度上限 8)。`!` 前缀为否定:值必须**不**命中该模式;只含否定的键在键存在且全部否定未命中时命中(YAML 里请给 `!` 模式加引号:`"!git*"`)。params 模式中 `/` 是普通字符,`*` 可跨 `/`。 |
|
|
28
29
|
| `paths` | `string[]` | 工作区相对路径模式,任一候选命中任一模式即可。候选来自参数中下列键的值(**任意嵌套深度**、深度上限):`path` `paths` `file` `files` `file_path` `dir` `directory` `directories` `cwd` `workspace` `root` `target` `targets` `output`。工作区外的绝对候选被丢弃;相对 `../` 形式保留,可显式匹配工作区外。路径模式中 `*` 不跨 `/`,`**` 跨任意深度(含零层)。`caseInsensitivePaths` 开启时(Windows 默认)根比较与模式匹配忽略 ASCII 大小写。 |
|
|
29
30
|
| `absent` | `string[]` | 必须**缺席**的参数键;**每个列出的键都必须缺失**(非对象参数天然满足)。 |
|
|
@@ -97,7 +98,11 @@ rules:
|
|
|
97
98
|
|
|
98
99
|
## 加载与热更新
|
|
99
100
|
|
|
100
|
-
- 按会话 cwd 惰性发现,首次 `tools/pre-execute` 时加载并缓存(超出 `maxCachedWorkspaces`
|
|
101
|
+
- 按会话 cwd 惰性发现,首次 `tools/pre-execute` 时加载并缓存(超出 `maxCachedWorkspaces` 逐出最久未用项);缓存键为解析后的 cwd(Windows 上折叠大小写),同一目录的不同拼写共享同一条目。
|
|
101
102
|
- 非法 YAML / 未知字段或 action / 坏 glob 与正则 / 易回溯灾难的模式 / 超 `maxRules` 都是加载期错误:`badFilePolicy: fail`(默认)时首个使用该工作区的工具调用响亮失败;`ignore-with-warning` 时告警并降级为空规则集。
|
|
102
103
|
- 绝对 `rulesFile` 或配置的 `fallbackPath` 在插件挂载时即校验,缺失/非法直接挂载失败。
|
|
103
104
|
- 文件变更后经 Chokidar 去抖重读(`watch`、`watchStabilityThresholdMs`);重读失败保留旧规则、只告警,绝不崩溃。`/rules reload` 手动触发同样路径;`/rules decisions` 回放审计轨迹,`/rules test` 对假设调用干跑规则。
|
|
105
|
+
- 预期存在但缺失的规则文件(未生效的项目文件、被删除后的 fallback)会通过其最近存在祖先目录被监听:会话中途创建即自动采纳,无需手动重载。`searchUp` 下只监听 cwd 层级的候选——更上层祖先新建规则文件仍需 `/rules reload`。
|
|
106
|
+
- `/rules` 列出当前规则(`list` 为显式别名);多文件链中每条规则行标注自己的来源文件。
|
|
107
|
+
- `/rules test` 支持前置标志:`--cwd <目录>` 换一个工作区求值(规则发现与路径归一化都切换);`--env 键=值`(可重复)覆盖宿主环境变量以测试 `when.env`;`--agent <选择器>`(可重复)为 `agents` 维度提供身份候选;`--platform <平台名>` 覆盖宿主平台以测试 `when.platform`。
|
|
108
|
+
- `enforce: false` 让插件进入干跑模式:deny/ask 命中只写审计(带 `dryRun` 标记,记录「本会做什么」与下游真实结果),所有调用照常透传——用于在生产环境先试跑新策略再强制执行。激活期间 `/rules` 会打印干跑提示。
|
|
@@ -54,6 +54,11 @@
|
|
|
54
54
|
"type": "array",
|
|
55
55
|
"items": { "type": "string", "minLength": 1 }
|
|
56
56
|
},
|
|
57
|
+
"agents": {
|
|
58
|
+
"description": "Agent-identity selector globs (main, subagent, preset:<name>); any selector matching any candidate satisfies the dimension. Unknown identity never matches.",
|
|
59
|
+
"type": "array",
|
|
60
|
+
"items": { "type": "string", "minLength": 1 }
|
|
61
|
+
},
|
|
57
62
|
"params": {
|
|
58
63
|
"description": "Param key to pattern(s). EVERY key must be present and match; a !-prefixed pattern negates. Scalars are stringified; arrays and nested objects contribute their scalar leaves.",
|
|
59
64
|
"type": "object",
|