@appchy/jarvis 0.1.84 → 0.1.85
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 +1 -1
- package/harness/harness/autonomy.py +20 -8
- package/harness/harness/branches.py +20 -7
- package/harness/harness/config.py +11 -1
- package/harness/harness/coverage.py +13 -4
- package/harness/harness/epic.py +16 -5
- package/harness/harness/events.py +18 -4
- package/harness/harness/git.py +58 -19
- package/harness/harness/lint.py +9 -4
- package/harness/harness/report.py +2 -54
- package/harness/harness/safety.py +12 -1
- package/harness/harness/shard.py +7 -1
- package/harness/harness/shift.py +9 -3
- package/harness/harness/task.py +11 -5
- package/harness/test_work.py +692 -1
- package/harness/work.py +38 -51
- package/package.json +7 -7
package/harness/work.py
CHANGED
|
@@ -79,7 +79,6 @@ Subcommands:
|
|
|
79
79
|
wrap [--task <name>] finish a session cleanly: what a machine knows
|
|
80
80
|
about this run, then what to do. Writes NOTHING
|
|
81
81
|
coverage [--feature <name>] what a RUN proved vs what features promise
|
|
82
|
-
migrate-owner [--force] rename `product:` -> `owner:` across tasks
|
|
83
82
|
|
|
84
83
|
The unattended loop — a schedule calls `next`, and the rest exists so a run that
|
|
85
84
|
cannot finish has somewhere to put the reason instead of stalling or guessing:
|
|
@@ -143,7 +142,7 @@ from harness import ids
|
|
|
143
142
|
from harness.architecture import (cmd_domain_new, cmd_init, cmd_rules, cmd_system_new,
|
|
144
143
|
cmd_where)
|
|
145
144
|
from harness.coverage import cmd_coverage
|
|
146
|
-
from harness.report import cmd_align, cmd_list,
|
|
145
|
+
from harness.report import cmd_align, cmd_list, cmd_readme
|
|
147
146
|
from harness.wrap import cmd_wrap
|
|
148
147
|
from harness.config import (DEFAULTS, ConfigError, apply, cmd_applies, cmd_config,
|
|
149
148
|
cmd_context, cmd_method, cmd_remind, resolve)
|
|
@@ -166,7 +165,7 @@ SUBCOMMANDS = (
|
|
|
166
165
|
"epic-new", "feature-new", "version-new", "place", "handoff", "release", "archive",
|
|
167
166
|
"find", "list", "readme", "move", "plan", "session", "kickoff", "path", "code",
|
|
168
167
|
"domain-new", "system-new", "where", "rules", "align", "wrap", "coverage",
|
|
169
|
-
"migrate", "
|
|
168
|
+
"migrate", "next", "status", "drop", "ask", "answer", "needs", "verify",
|
|
170
169
|
"observed", "log", "digest", "sync",
|
|
171
170
|
)
|
|
172
171
|
|
|
@@ -192,27 +191,32 @@ def parse_argv(argv):
|
|
|
192
191
|
|
|
193
192
|
|
|
194
193
|
def _project_root(flags) -> Path:
|
|
195
|
-
"""The repo the harness is acting on
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
194
|
+
"""The repo the harness is acting on: `--project`, else the repo the WORK TREE
|
|
195
|
+
was found in, else the env, else the cwd.
|
|
196
|
+
|
|
197
|
+
**It asks the tree search rather than re-reading the environment**, and that is
|
|
198
|
+
the whole of it. This used to consult the same two variables in the OPPOSITE
|
|
199
|
+
order — `CLAUDE_PROJECT_DIR` first, `WORK_DIR` second — while claiming in this
|
|
200
|
+
docstring to match the tree search. With both set at once, which they are under
|
|
201
|
+
Claude Code, config resolved from one repo while the tree being mutated was in
|
|
202
|
+
another: every id, every standard and every gate ran in a dialect the tree's
|
|
203
|
+
own repo never chose. One question about which repo this is, asked once.
|
|
204
|
+
|
|
205
|
+
The fallbacks are what keeps `init` and `doctor` working on a repo that has no
|
|
206
|
+
tree yet — there is nothing for the search to find, so the env and then the cwd
|
|
207
|
+
answer instead. And the cwd step WALKS UP: standing one directory into a repo
|
|
208
|
+
used to find no config at all, so `align` from the root reported twenty
|
|
209
|
+
citations and from `apps/cli/` thirteen entirely different ones.
|
|
206
210
|
"""
|
|
207
211
|
import os
|
|
208
|
-
p = flags.get("project")
|
|
212
|
+
p = flags.get("project")
|
|
209
213
|
if p:
|
|
210
214
|
return Path(p).resolve()
|
|
211
|
-
wd = os.environ.get("WORK_DIR")
|
|
212
|
-
if wd:
|
|
213
|
-
return Path(wd).resolve().parent
|
|
214
215
|
root, _, cur = locate_work_root()
|
|
215
|
-
|
|
216
|
+
if root:
|
|
217
|
+
return root.parent
|
|
218
|
+
project = os.environ.get("CLAUDE_PROJECT_DIR")
|
|
219
|
+
return Path(project).resolve() if project else cur
|
|
216
220
|
|
|
217
221
|
|
|
218
222
|
def main() -> int:
|
|
@@ -272,22 +276,23 @@ def main() -> int:
|
|
|
272
276
|
cfg = copy.deepcopy(DEFAULTS)
|
|
273
277
|
apply(cfg)
|
|
274
278
|
|
|
275
|
-
#
|
|
276
|
-
#
|
|
277
|
-
#
|
|
278
|
-
#
|
|
279
|
-
#
|
|
280
|
-
|
|
279
|
+
# THE COMMIT BELONGS TO A COMMAND THAT CHANGES THE BOARD, and to nothing else
|
|
280
|
+
# (founder, 2026-09-10). Every other command — `list`, `status`, `where`,
|
|
281
|
+
# `context`, `align`, and the two a Claude Code hook fires — only looks, so it
|
|
282
|
+
# has nothing of its own to land. They used to drive the commit anyway, from a
|
|
283
|
+
# `finally:` that ran for all of them: what got committed was whatever the
|
|
284
|
+
# person happened to have open in `work/` at that moment, filed as
|
|
285
|
+
# "docs(work): board edits" under no item, at a moment nobody chose. The pull
|
|
286
|
+
# was already scoped this way; now both halves ask the same question.
|
|
287
|
+
if cmd not in git.WRITES or not git.enabled():
|
|
281
288
|
return dispatch(cmd, pos, flags, cfg)
|
|
282
289
|
|
|
283
|
-
# Pull → write → commit → push. The commit
|
|
284
|
-
#
|
|
285
|
-
#
|
|
286
|
-
# the hole this closes.
|
|
290
|
+
# Pull → write → commit → push. The commit runs in a `finally`: a command that
|
|
291
|
+
# mutated the tree and then exited non-zero has still changed the board, and
|
|
292
|
+
# leaving that uncommitted is exactly the hole this closes.
|
|
287
293
|
root = locate_work_root()[0]
|
|
288
294
|
repo = root.parent if root else _project_root(flags)
|
|
289
|
-
|
|
290
|
-
_say(git.refresh(repo))
|
|
295
|
+
_say(git.refresh(repo))
|
|
291
296
|
try:
|
|
292
297
|
return dispatch(cmd, pos, flags, cfg)
|
|
293
298
|
finally:
|
|
@@ -298,9 +303,8 @@ def main() -> int:
|
|
|
298
303
|
# whole seam exists to close.
|
|
299
304
|
rows = events.pending()
|
|
300
305
|
# The item is what an event names, or what the command was given. A command
|
|
301
|
-
# with neither
|
|
302
|
-
#
|
|
303
|
-
# an item.
|
|
306
|
+
# with neither names NO item, rather than putting a command name where four
|
|
307
|
+
# other slices expect an item.
|
|
304
308
|
item = rows[0]["name"] if rows else (pos[0] if pos else "")
|
|
305
309
|
committed, pushed, note = git.land(repo, item, rows)
|
|
306
310
|
if committed:
|
|
@@ -321,21 +325,6 @@ def _say(note: str) -> None:
|
|
|
321
325
|
sys.stderr.flush()
|
|
322
326
|
|
|
323
327
|
|
|
324
|
-
#: Commands a HOOK fires rather than a person, and which therefore run at a moment
|
|
325
|
-
#: nobody picked. They are kept clear of the commit that every other command drives:
|
|
326
|
-
#: a hook is not a decision to publish the board.
|
|
327
|
-
#:
|
|
328
|
-
#: NOT folded into `_READS`, though every name here is in it. That set is "commands
|
|
329
|
-
#: that only look", and one of its members is `align`, which is deliberately expected
|
|
330
|
-
#: to sweep up somebody's hand edit and commit it — the commit site says so in as many
|
|
331
|
-
#: words. Reusing it would turn a read-set into a commit-policy and change that
|
|
332
|
-
#: behaviour silently. `remind` has the same shape as this one and is deliberately
|
|
333
|
-
#: left out: it fires once at the end of a turn rather than before every edit, and
|
|
334
|
-
#: whether the Stop hook should be committing the board is a question somebody should
|
|
335
|
-
#: answer on purpose rather than have settled as a side effect of this.
|
|
336
|
-
_HOOK_PATH = {"applies"}
|
|
337
|
-
|
|
338
|
-
|
|
339
328
|
#: Commands that only LOOK. `--branch` is meaningful on these and refused on every
|
|
340
329
|
#: other, which is the safe default: a command added later is covered without anyone
|
|
341
330
|
#: remembering to come back here.
|
|
@@ -506,8 +495,6 @@ def dispatch(cmd, pos, flags, cfg) -> int:
|
|
|
506
495
|
return cmd_coverage(flags)
|
|
507
496
|
if cmd == "migrate":
|
|
508
497
|
return cmd_migrate(flags)
|
|
509
|
-
if cmd == "migrate-owner":
|
|
510
|
-
return cmd_migrate_owner(flags)
|
|
511
498
|
|
|
512
499
|
# --- the unattended loop -------------------------------------------------
|
|
513
500
|
# `next` is the entry point a schedule calls; everything else here exists so
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@appchy/jarvis",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.85",
|
|
4
4
|
"description": "Jarvis — local AI coding assistant CLI",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
@@ -57,16 +57,16 @@
|
|
|
57
57
|
"typescript": "^5.7.0",
|
|
58
58
|
"vitest": "^2.1.0",
|
|
59
59
|
"@jarvis/agents": "1.0.0",
|
|
60
|
-
"@jarvis/data": "0.1.0",
|
|
61
|
-
"@jarvis/errors": "1.0.0",
|
|
62
60
|
"@jarvis/anthropic": "1.0.0",
|
|
63
61
|
"@jarvis/board": "0.1.0",
|
|
64
|
-
"@jarvis/
|
|
62
|
+
"@jarvis/data": "0.1.0",
|
|
63
|
+
"@jarvis/errors": "1.0.0",
|
|
65
64
|
"@jarvis/rpc": "1.0.0",
|
|
66
|
-
"@jarvis/
|
|
65
|
+
"@jarvis/logger": "1.0.0",
|
|
67
66
|
"@jarvis/types": "1.0.0",
|
|
68
|
-
"@jarvis/
|
|
69
|
-
"@jarvis/ui": "0.1.0"
|
|
67
|
+
"@jarvis/typescript-config": "1.0.0",
|
|
68
|
+
"@jarvis/ui": "0.1.0",
|
|
69
|
+
"@jarvis/vitest-config": "1.0.0"
|
|
70
70
|
},
|
|
71
71
|
"scripts": {
|
|
72
72
|
"dev": "tsx watch src/bin.ts start --foreground",
|