riskrails 4.1.0 → 4.3.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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/README.md +25 -15
- package/agents/ai-reviewer.md.tmpl +14 -1
- package/ai/agents/implementer.md +6 -0
- package/ai/agents/reviewer.md +8 -1
- package/bin/ansi.js +12 -0
- package/bin/menu.js +71 -12
- package/bin/riskrails.js +240 -14
- package/bin/uninstall.js +115 -0
- package/bin/wizard.js +72 -0
- package/hooks/AGENTS.md +0 -6
- package/hooks/CLAUDE.md +0 -6
- package/hooks/context-guard.py +11 -1
- package/hooks/dialog-gate.py +3 -2
- package/hooks/guard/decision.py +4 -3
- package/hooks/guard/dialog.py +12 -2
- package/hooks/guard/ere.py +29 -10
- package/hooks/guard/git.py +2 -1
- package/hooks/guard/paths.py +48 -1406
- package/hooks/guard/paths_base.py +66 -0
- package/hooks/guard/paths_bash.py +176 -0
- package/hooks/guard/paths_classify.py +205 -0
- package/hooks/guard/paths_fs.py +137 -0
- package/hooks/guard/paths_human.py +105 -0
- package/hooks/guard/paths_interp.py +162 -0
- package/hooks/guard/paths_movers.py +393 -0
- package/hooks/guard/paths_shell.py +288 -0
- package/hooks/runtime-gate.py +87 -0
- package/hooks/update_notice.py +23 -8
- package/package.json +1 -1
- package/skills/AGENTS.md +0 -6
- package/skills/CLAUDE.md +0 -6
- package/skills/ai-init/templates/.ai/policies/coding.md +9 -0
- package/skills/ai-init/templates/gitignore.snippet +2 -0
- package/skills/ai-task/memory_cleanup.py +213 -0
- package/skills/ai-task/memory_compact.py +25 -0
- package/skills/ai-task/memory_config.py +120 -0
- package/skills/ai-task/memory_export.py +246 -0
- package/skills/ai-task/memory_format.py +7 -3
- package/skills/ai-task/memory_hook.py +172 -0
- package/skills/ai-task/memory_io.py +122 -0
- package/skills/ai-task/memory_migrate.py +187 -0
- package/skills/ai-task/memory_packages.py +83 -0
- package/skills/ai-task/memory_risks.py +84 -0
- package/skills/ai-task/memory_status.py +76 -0
- package/skills/ai-task/memory_sync.py +20 -1481
- package/skills/ai-task/memory_verify.py +316 -0
- package/skills/ai-task/rr_state/cli.py +1 -0
- package/skills/ai-task/rr_state/close.py +2 -2
- package/skills/ai-task/rr_state/dialog.py +32 -7
- package/skills/ai-task/rr_state/dialog_cmd.py +38 -5
- package/skills/ai-task/rr_state/gate.py +10 -4
- package/skills/ai-task/rr_state/labels.py +33 -4
- package/skills/ai-task/rr_state/memory_cmd.py +84 -64
- package/skills/ai-task/rr_state/questions_cmd.py +5 -4
- package/skills/ai-task/rr_state/report.py +73 -10
- package/skills/ai-task/rr_state/report_cmd.py +2 -0
- package/skills/ai-task/rr_state/review.py +18 -14
- package/skills/ai-task/rr_state/start.py +12 -9
- package/skills/ai-task/rr_state/steps.py +16 -7
- package/skills/ai-task/rr_state/verify.py +17 -12
- package/skills/project-update/history/blobs/dac90781793b8a1c8e5f3e0307665b6394d922140c3831c5508d019f79e1ed0a +41 -0
- package/skills/project-update/history/blobs/fa0cb370b024e1a30e1355912032657b08f609e47773a603c193b1e609b0f997 +8 -0
- package/skills/project-update/history/index.json +8 -4
- package/skills/project-update/rr_update/__init__.py +1 -0
- package/skills/project-update/rr_update/apply.py +188 -0
- package/skills/project-update/rr_update/blocks.py +38 -0
- package/skills/project-update/rr_update/build.py +96 -0
- package/skills/project-update/rr_update/cleanup.py +194 -0
- package/skills/project-update/rr_update/cli.py +102 -0
- package/skills/project-update/rr_update/files.py +106 -0
- package/skills/project-update/rr_update/gate.py +60 -0
- package/skills/project-update/rr_update/hints.py +128 -0
- package/skills/project-update/rr_update/history.py +117 -0
- package/skills/project-update/rr_update/kept.py +120 -0
- package/skills/project-update/rr_update/paths.py +108 -0
- package/skills/project-update/rr_update/plan.py +123 -0
- package/skills/project-update/rr_update/report.py +51 -0
- package/skills/project-update/rr_update/rules.py +178 -0
- package/skills/project-update/rr_update/shared.py +74 -0
- package/skills/project-update/rr_update/siblings.py +37 -0
- package/skills/project-update/rr_update/weights.py +171 -0
- package/skills/project-update/update.py +8 -1682
- package/skills/rr-build/SKILL.md +5 -2
- package/skills/rr-setup/SKILL.md +4 -2
- package/skills/rr-setup/setup.py +13 -12
- package/skills/rr-ship/SKILL.md +2 -2
- package/skills/rr-spec/SKILL.md +6 -0
- package/skills/rr-status/SKILL.md +2 -0
- package/skills/rr-status/status.py +5 -3
package/hooks/guard/dialog.py
CHANGED
|
@@ -45,6 +45,7 @@ INTERPRETER_RE = re.compile(r"^python3?$")
|
|
|
45
45
|
INTERPRETER_FLAG_RE = re.compile(r"^-[IS]+$")
|
|
46
46
|
PUSH_REF_RE = re.compile(r"^[A-Za-z0-9._/-]+$")
|
|
47
47
|
NPX_PACKAGE_RE = re.compile(r"^riskrails@[0-9A-Za-z.+-]+$")
|
|
48
|
+
BATCH_ON = {"--by": True, "--why": False} # `batch on` options, each at most once; True = required. rr_state/labels.py keeps the same table
|
|
48
49
|
TS = "%Y-%m-%dT%H:%M:%SZ"
|
|
49
50
|
|
|
50
51
|
# hooks/guard/dialog.py -> the plugin root; the Stop reminder names its
|
|
@@ -217,12 +218,12 @@ def validate_label(label):
|
|
|
217
218
|
if state_label_ok(tokens):
|
|
218
219
|
return None
|
|
219
220
|
return ("a label starts with git commit, git push, a human-only state.py command (approve, reject, "
|
|
220
|
-
"profile set, verify/review/start --by, memory enable) or npx riskrails@<v>")
|
|
221
|
+
"profile set, verify/review/start --by, batch on --by, memory enable) or npx riskrails@<v>")
|
|
221
222
|
|
|
222
223
|
|
|
223
224
|
def state_label_ok(tokens):
|
|
224
225
|
"""python3 [-I|-S…] <…/state.py> approve|reject|profile set|verify --by|
|
|
225
|
-
review --by|start --mode … --by|memory enable [--author S] [--period month|quarter|year]
|
|
226
|
+
review --by|start --mode … --by|batch on --by S [--why S]|memory enable [--author S] [--period month|quarter|year]
|
|
226
227
|
[--index none|sqlite]: each option at most once, as two words, in any order, and no other
|
|
227
228
|
memory verb. rr_state/labels.py keeps the same rule; tests/test_dialog_contract.py pins the
|
|
228
229
|
two equal."""
|
|
@@ -236,6 +237,11 @@ def state_label_ok(tokens):
|
|
|
236
237
|
rest = tokens[i + 1:]
|
|
237
238
|
while rest[:1] in (["--root"], ["--runtime"]) and len(rest) >= 2:
|
|
238
239
|
rest = rest[2:]
|
|
240
|
+
if rest[:2] == ["batch", "on"]:
|
|
241
|
+
names, values = rest[2::2], rest[3::2]
|
|
242
|
+
return (len(names) == len(values) == len(set(names)) and set(names) <= set(BATCH_ON)
|
|
243
|
+
and all(n in names for n, need in BATCH_ON.items() if need)
|
|
244
|
+
and all(v[:1] not in ("", "-") for v in values))
|
|
239
245
|
if rest[:1] in (["approve"], ["reject"]) or rest[:2] == ["profile", "set"]:
|
|
240
246
|
return True
|
|
241
247
|
if rest[:1] in (["verify"], ["review"]):
|
|
@@ -589,6 +595,10 @@ def is_git_label(label):
|
|
|
589
595
|
return (label or "").split()[:1] == ["git"]
|
|
590
596
|
|
|
591
597
|
|
|
598
|
+
def is_npx_label(label):
|
|
599
|
+
return (label or "").split()[:1] == ["npx"]
|
|
600
|
+
|
|
601
|
+
|
|
592
602
|
def used_note(pick):
|
|
593
603
|
"""What a deny after the gate spent a git pick adds (review focus 1)."""
|
|
594
604
|
return ("\n\nThe pick %s was used by this call and is spent; the command did not run. To ask the human "
|
package/hooks/guard/ere.py
CHANGED
|
@@ -528,11 +528,18 @@ class _Found: # pylint: disable=too-few-public-met
|
|
|
528
528
|
|
|
529
529
|
|
|
530
530
|
class Compiled:
|
|
531
|
-
"""One pattern in one mode. `rx` is None when the pattern is invalid.
|
|
532
|
-
|
|
531
|
+
"""One pattern in one mode. `rx` is None when the pattern is invalid.
|
|
532
|
+
|
|
533
|
+
The pattern is parsed here (parse errors are caught here), but the regex
|
|
534
|
+
and the DFA are built on the first read of `rx`: `search` runs the cheap
|
|
535
|
+
`required` prefilter first, so a subject it rejects never pays for
|
|
536
|
+
`re.compile` (a fresh process holds ~34 patterns, most never get past it).
|
|
537
|
+
"""
|
|
538
|
+
__slots__ = ("_rx", "_pending", "required", "anchored", "dfa")
|
|
533
539
|
|
|
534
540
|
def __init__(self, pattern, mode, capture):
|
|
535
|
-
self.
|
|
541
|
+
self._rx = None
|
|
542
|
+
self._pending = None # (tree, mode, capture) until built
|
|
536
543
|
self.required = None
|
|
537
544
|
self.anchored = False
|
|
538
545
|
self.dfa = None
|
|
@@ -546,18 +553,30 @@ class Compiled:
|
|
|
546
553
|
tree = _strip_leading(tree)
|
|
547
554
|
elif capture:
|
|
548
555
|
self.anchored = _starts_with_dotstar(tree)
|
|
549
|
-
|
|
550
|
-
self.rx = re.compile(_emit(tree, mode), flags)
|
|
551
|
-
if not capture and _nested(tree) and _dfa_ok(tree):
|
|
552
|
-
self.dfa = _Dfa(tree, mode)
|
|
556
|
+
self._pending = (tree, mode, capture)
|
|
553
557
|
except (EreError, re.error, RecursionError, OverflowError):
|
|
554
|
-
self.
|
|
558
|
+
self._pending = None
|
|
559
|
+
|
|
560
|
+
@property
|
|
561
|
+
def rx(self):
|
|
562
|
+
if self._pending is not None:
|
|
563
|
+
tree, mode, capture = self._pending
|
|
564
|
+
self._pending = None
|
|
565
|
+
try:
|
|
566
|
+
flags = re.ASCII if mode == "b" else 0
|
|
567
|
+
self._rx = re.compile(_emit(tree, mode), flags)
|
|
568
|
+
if not capture and _nested(tree) and _dfa_ok(tree):
|
|
569
|
+
self.dfa = _Dfa(tree, mode)
|
|
570
|
+
except (EreError, re.error, RecursionError, OverflowError):
|
|
571
|
+
self._rx = None
|
|
572
|
+
self.dfa = None
|
|
573
|
+
return self._rx
|
|
555
574
|
|
|
556
575
|
def search(self, subject):
|
|
557
|
-
if self.rx is None:
|
|
558
|
-
return None
|
|
559
576
|
if self.required is not None and not any(r in subject for r in self.required):
|
|
560
577
|
return None
|
|
578
|
+
if self.rx is None:
|
|
579
|
+
return None
|
|
561
580
|
if self.dfa is not None:
|
|
562
581
|
return _Found() if self.dfa.search(subject) else None
|
|
563
582
|
return self.rx.match(subject) if self.anchored else self.rx.search(subject)
|
package/hooks/guard/git.py
CHANGED
|
@@ -285,7 +285,8 @@ class GitGuard:
|
|
|
285
285
|
return
|
|
286
286
|
except Exception: # pylint: disable=broad-exception-caught
|
|
287
287
|
pass
|
|
288
|
-
raise Deny(text_to_b("CONSENT_REQUIRED \u2014 no live pick of this session is this command.
|
|
288
|
+
raise Deny(text_to_b("CONSENT_REQUIRED \u2014 no live pick of this session is this command. Ask the human in "
|
|
289
|
+
"the dialog; or they run it in their own terminal.") + WHY_CONSENT)
|
|
289
290
|
|
|
290
291
|
def _drop_status(self, path):
|
|
291
292
|
"""`[ "${#path}" -lt 3 ] || path="${path:3}"`, counted in the locale's characters."""
|