@appchy/jarvis 0.1.42 → 0.1.43

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/dist/bin.js CHANGED
@@ -9612,7 +9612,7 @@ import WebSocket from "ws";
9612
9612
  import { createRequire as createRequire2 } from "module";
9613
9613
  var _require = createRequire2(import.meta.url);
9614
9614
  var VERSION2 = _require("../package.json").version ?? "0.0.0";
9615
- var SHA = "9d8714f";
9615
+ var SHA = "c87ba61";
9616
9616
  var BUILT = "2026-09-07";
9617
9617
  var BUILD = SHA ?? "source";
9618
9618
  var BUILD_LABEL = SHA ? `${VERSION2} (${SHA}${BUILT ? ` ${BUILT}` : ""})` : `${VERSION2} (source)`;
@@ -85,6 +85,26 @@ def _git(argv, stdin=""):
85
85
  if sub == "checkout" and rest and rest[0] in (".", "--"):
86
86
  return ("`git checkout .` discards every uncommitted change.",
87
87
  "`git stash` keeps them.")
88
+ # `restore` is the modern spelling of the line above and destroys identically —
89
+ # git's own docs steer people to it, so covering one and not the other left the
90
+ # rule one synonym deep. It needs no `.`/`--` test: `restore` only ever takes
91
+ # paths, so there is no branch form to mistake a path for.
92
+ #
93
+ # `--staged` alone is the exception and it is a real one: that writes the index
94
+ # and leaves the working tree untouched, which is somebody unstaging a file.
95
+ # Denying it would spend the guard's authority on a safe, common command.
96
+ if sub == "restore" and rest:
97
+ touches_worktree = "--worktree" in rest or "-W" in rest or not (
98
+ "--staged" in rest or "-S" in rest)
99
+ if touches_worktree:
100
+ return ("`git restore` overwrites the working tree from another commit, "
101
+ "discarding uncommitted changes with no reflog entry.",
102
+ "`git stash` keeps them, or `git restore --staged` to unstage "
103
+ "without touching the file.")
104
+ if sub == "stash" and rest and rest[0] == "clear":
105
+ return ("`git stash clear` deletes every stash on this machine at once, and "
106
+ "nothing prints what they were.",
107
+ "`git stash list` first, then `git stash drop` the one you mean.")
88
108
  return None
89
109
 
90
110
 
@@ -132,6 +152,10 @@ DENY = (_rm, _git, _sql, _fs)
132
152
  #: fine here: the cost of a false positive is one extra line of output.
133
153
  WARN = {
134
154
  "--no-verify": "skips the pre-commit hooks this repo installed on purpose",
155
+ # Recoverable and therefore not denied: `drop` prints the sha it dropped, so the
156
+ # work is reachable until it is collected. Named anyway, because the sha is only
157
+ # useful to somebody who noticed it go past.
158
+ "git stash drop": "deletes a stash — the sha it prints is the only way back",
135
159
  "npm publish": "publishes to the registry — a version number cannot be reused",
136
160
  "pnpm publish": "publishes to the registry — a version number cannot be reused",
137
161
  "terraform apply": "changes real infrastructure",
@@ -2807,6 +2807,30 @@ def test_the_guard_blocks_the_unrecoverable_and_allows_everything_else():
2807
2807
  assert guard.check("git commit --no-verify -m x")[0] == "warn"
2808
2808
 
2809
2809
 
2810
+ def test_a_destructive_git_verb_is_covered_by_both_its_spellings():
2811
+ # `git restore` is what git's own docs steer people to, and it destroys exactly
2812
+ # what `git checkout --` destroys. Covering one and not the other made the rule
2813
+ # one synonym deep, and the synonym is the one a newer hand reaches for first.
2814
+ # Measured cost of the gap: a session in another repo ran `git checkout -- work/`
2815
+ # here and took a 68-line rule that had never been committed.
2816
+ guard = _load_guard()
2817
+ for bad in ("git restore work/", "git restore .",
2818
+ "git restore --worktree src/a.ts",
2819
+ "git restore --staged --worktree src/a.ts",
2820
+ "git restore --source=HEAD~1 src/a.ts",
2821
+ "git stash clear"):
2822
+ assert guard.check(bad)[0] == "deny", f"should have blocked: {bad}"
2823
+
2824
+ # `--staged` on its own writes the index and leaves the file alone — somebody
2825
+ # unstaging, which is safe and common. Denying it would spend the guard's
2826
+ # authority on ordinary work, and an authority-less guard protects nothing.
2827
+ for ok in ("git restore --staged src/a.ts", "git restore -S src/a.ts"):
2828
+ assert guard.check(ok)[0] != "deny", f"false positive: {ok}"
2829
+
2830
+ # Recoverable, so it is named rather than refused: `drop` prints the sha.
2831
+ assert guard.check("git stash drop")[0] == "warn"
2832
+
2833
+
2810
2834
  def test_a_quoted_separator_does_not_switch_the_guard_off():
2811
2835
  # The headline hole: `re.split` cut at the `;` INSIDE the string, both halves
2812
2836
  # then failed to parse as unbalanced quotes, and every rule was skipped. A
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@appchy/jarvis",
3
- "version": "0.1.42",
3
+ "version": "0.1.43",
4
4
  "description": "Jarvis — local AI coding assistant CLI",
5
5
  "private": false,
6
6
  "type": "module",
@@ -51,13 +51,13 @@
51
51
  "vitest": "^2.1.0",
52
52
  "@jarvis/agents": "1.0.0",
53
53
  "@jarvis/anthropic": "1.0.0",
54
+ "@jarvis/board": "0.1.0",
55
+ "@jarvis/data": "0.1.0",
54
56
  "@jarvis/logger": "1.0.0",
55
57
  "@jarvis/rpc": "1.0.0",
56
- "@jarvis/board": "0.1.0",
57
58
  "@jarvis/types": "1.0.0",
58
59
  "@jarvis/typescript-config": "1.0.0",
59
- "@jarvis/vitest-config": "1.0.0",
60
- "@jarvis/data": "0.1.0"
60
+ "@jarvis/vitest-config": "1.0.0"
61
61
  },
62
62
  "scripts": {
63
63
  "dev": "tsx watch src/bin.ts start --foreground",