@appchy/jarvis 0.1.94 → 0.1.95

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
@@ -10154,7 +10154,7 @@ import { createRequire as createRequire2 } from "module";
10154
10154
  var _require = createRequire2(import.meta.url);
10155
10155
  var VERSION2 = _require("../package.json").version ?? "0.0.0";
10156
10156
  var IS_DEV = String(_require("../package.json").name ?? "").endsWith("-dev");
10157
- var SHA = "7b77d4e";
10157
+ var SHA = "20f7aad";
10158
10158
  var BUILT = "2026-09-11";
10159
10159
  var BUILD = SHA ?? "source";
10160
10160
  var BUILD_LABEL = `${SHA ? `${VERSION2} (${SHA}${BUILT ? ` ${BUILT}` : ""})` : `${VERSION2} (source)`}${IS_DEV ? " \u2014 development build" : ""}`;
@@ -1385,6 +1385,12 @@ def _standing_lines(where) -> list:
1385
1385
  f"{shown}{more}")
1386
1386
  for cut in where.cuts:
1387
1387
  out.append(f"cut {cut} — every task complete, not released")
1388
+ # Said LAST and only beside something else. A repo git cannot read has nothing
1389
+ # actionable to say about commits, so on its own this would be a line every turn
1390
+ # that nobody can act on — while next to work in flight it is the difference
1391
+ # between "your code is committed" and "nobody checked".
1392
+ if where.loose is None and out:
1393
+ out.append("code in git: cannot tell — git did not answer in this directory")
1388
1394
  return out
1389
1395
 
1390
1396
 
@@ -20,7 +20,7 @@ say what's actually completed or not and where are we standing"_. So the bottom
20
20
  derived — the bucket, the criteria, what the completion gate would still refuse on — and
21
21
  the prose is left to say whatever it says.
22
22
  """
23
- from datetime import date, timedelta
23
+ from datetime import datetime, timedelta, timezone
24
24
  from pathlib import Path
25
25
  from typing import NamedTuple
26
26
 
@@ -44,14 +44,12 @@ class Standing(NamedTuple):
44
44
  held: list
45
45
  #: Names taken here and moved to complete.
46
46
  finished: list
47
- #: Paths outside the board that are not in git.
47
+ #: Paths outside the board that are not in git — None when git could not answer,
48
+ #: which is not the same as none of them.
48
49
  loose: list
49
50
  #: Cuts whose every task is complete and which nobody has released.
50
51
  cuts: list
51
52
 
52
- def anything(self) -> bool:
53
- return bool(self.held or self.finished or self.loose or self.cuts)
54
-
55
53
 
56
54
  #: How far back to read the board's own history when working out what THIS session
57
55
  #: took. A session does not outlive a few days, and the log it is read out of grows
@@ -88,16 +86,24 @@ def standing(session: str, repo: Path) -> Standing:
88
86
  # tree is kept; whether a session's code reached git is a question about the
89
87
  # repo, and it is the one worth answering in a repo that has not switched the
90
88
  # board's own commits on.
91
- loose = git.uncommitted_code(repo)
89
+ #
90
+ # None when git cannot answer, which is NOT the same as nothing outstanding —
91
+ # the list comes back empty from a repo git cannot read, and an empty list here
92
+ # would read as "your code is safe". The caller says which it got.
93
+ loose = git.uncommitted_code(repo) if git.is_repo(repo) else None
92
94
  root, _, _ = locate_work_root()
93
95
  if not root or not root.is_dir():
94
96
  return Standing(held=[], finished=[], loose=loose, cuts=[])
95
97
 
96
- since = (date.today() - timedelta(days=_WINDOW_DAYS)).isoformat()
97
- mine = {}
98
- for e in read_events(root, since=since):
99
- if e.get("event") == "moved" and e.get("by") == session and e.get("name"):
100
- mine[e["name"]] = e.get("to")
98
+ # UTC, because that is the clock the rows are on: git anchors a plain date to
99
+ # UTC midnight and the file backend compares it as text against a UTC
100
+ # timestamp, so a local date would move the window by this machine's offset.
101
+ since = (datetime.now(timezone.utc).date()
102
+ - timedelta(days=_WINDOW_DAYS)).isoformat()
103
+ # Where it went is deliberately not kept: the bucket the item is in NOW is the
104
+ # status, and a remembered destination would be a second answer to it.
105
+ mine = {e["name"] for e in read_events(root, since=since)
106
+ if e.get("event") == "moved" and e.get("by") == session and e.get("name")}
101
107
 
102
108
  held, finished = [], []
103
109
  for name in mine:
@@ -144,8 +150,7 @@ def _uncommitted(repo) -> list:
144
150
  comes back empty from a repo git cannot read — and reporting that as "nothing
145
151
  uncommitted" is the one wrong answer this section must not give.
146
152
  """
147
- code, _, _ = git._git(repo, "rev-parse", "--git-dir")
148
- if code != 0:
153
+ if not git.is_repo(repo):
149
154
  return [" code in git: cannot tell — this is not a git repo, or git did not "
150
155
  "answer. Check it yourself before you walk away."]
151
156
  # One reader for what is outside the board, shared with the end-of-turn line and
@@ -189,10 +194,12 @@ def _in_flight(root) -> list:
189
194
  # The ratio, because "move what finished" needs to know which of these is
190
195
  # anywhere near finished, and the criteria are the only answer to that which
191
196
  # does not depend on somebody's recollection.
192
- named = "\n ".join(
193
- f"{t.name} {t.title}"
194
- + (f" [{total - len(unchecked)}/{total} criteria]" if total else "")
195
- for t in live for unchecked, total in [gate.criteria(t)])
197
+ rows = []
198
+ for t in live:
199
+ unchecked, total = gate.criteria(t)
200
+ ratio = f" [{total - len(unchecked)}/{total} criteria]" if total else ""
201
+ rows.append(f"{t.name} — {t.title}{ratio}")
202
+ named = "\n ".join(rows)
196
203
  return [f" in progress: {len(live)} item(s). Move what finished, park what did "
197
204
  f"not — the bucket IS the status:\n {named}"]
198
205
 
package/harness/work.py CHANGED
@@ -64,9 +64,10 @@ Subcommands:
64
64
  config set <key> <value> [--json] write one dotted key, validated first
65
65
  config unset <key> drop an override, back to the default
66
66
  context [--project <dir>] the SessionStart pointers, all config-derived
67
- remind --used <tokens> [--session <id>] wrap up yet? one JSON object, or
68
- nothing. Takes a MEASUREMENT: how full a
69
- session is, is its client's to answer
67
+ remind [--used <tokens>] [--session <id>] where the work stands, and whether
68
+ to wrap up. One JSON object, or nothing. The
69
+ measurement is OPTIONAL and is its client's to
70
+ answer; the tree and git need none
70
71
  applies --file <path> [--session <id>] what a session must be told now that
71
72
  it is about to write this file — the judgements
72
73
  no gate catches, and which system it is in
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@appchy/jarvis",
3
- "version": "0.1.94",
3
+ "version": "0.1.95",
4
4
  "description": "Jarvis — local AI coding assistant CLI",
5
5
  "private": false,
6
6
  "type": "module",