fusion-cli 1.2.1__tar.gz → 1.3.2__tar.gz
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.
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/PKG-INFO +6 -3
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/README.md +5 -2
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/pyproject.toml +1 -1
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/src/fusion/cli.py +29 -0
- fusion_cli-1.3.2/src/fusion/update.py +85 -0
- fusion_cli-1.3.2/tests/test_cli_update.py +140 -0
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/uv.lock +1 -1
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/.gitignore +0 -0
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/hatch_build.py +0 -0
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/skills/fusion-analyst/SKILL.md +0 -0
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/skills/fusion-analyst/references/assess.md +0 -0
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/skills/fusion-analyst/references/compare.md +0 -0
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/skills/fusion-analyst/references/export.md +0 -0
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/skills/fusion-analyst/references/fusion-conventions.md +0 -0
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/skills/fusion-analyst/references/report.md +0 -0
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/skills/fusion-analyst/scripts/export.py +0 -0
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/skills/fusion-intake/SKILL.md +0 -0
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/skills/fusion-intake/references/convert.md +0 -0
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/skills/fusion-intake/references/delivery.md +0 -0
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/skills/fusion-intake/references/fusion-conventions.md +0 -0
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/skills/fusion-intake/references/gate.md +0 -0
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/skills/fusion-intake/scripts/convert.py +0 -0
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/skills/fusion-intake/scripts/gate.py +0 -0
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/skills/fusion-intake/tests/conftest.py +0 -0
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/skills/fusion-intake/tests/make_fixtures.py +0 -0
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/skills/fusion-intake/tests/test_convert.py +0 -0
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/skills/fusion-intake/tests/test_gate.py +0 -0
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/skills/fusion-intake/tests/test_integration.py +0 -0
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/skills/fusion-librarian/SKILL.md +0 -0
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/skills/fusion-librarian/references/archive.md +0 -0
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/skills/fusion-librarian/references/create.md +0 -0
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/skills/fusion-librarian/references/cross-reference.md +0 -0
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/skills/fusion-librarian/references/fusion-conventions.md +0 -0
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/skills/fusion-librarian/references/promote.md +0 -0
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/skills/fusion-librarian/references/query.md +0 -0
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/skills/fusion-librarian/references/reflect.md +0 -0
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/skills/fusion-librarian/references/restructure.md +0 -0
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/skills/fusion-librarian/references/tag.md +0 -0
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/skills/fusion-librarian/scripts/link-repair.py +0 -0
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/skills/fusion-librarian/tests/conftest.py +0 -0
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/skills/fusion-librarian/tests/test_link_repair.py +0 -0
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/skills/fusion-planner/SKILL.md +0 -0
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/skills/fusion-planner/references/close.md +0 -0
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/skills/fusion-planner/references/create-activity.md +0 -0
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/skills/fusion-planner/references/fusion-conventions.md +0 -0
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/skills/fusion-planner/references/horizon.md +0 -0
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/src/fusion/__init__.py +0 -0
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/src/fusion/bucket.py +0 -0
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/src/fusion/checker.py +0 -0
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/src/fusion/document.py +0 -0
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/src/fusion/hub.py +0 -0
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/src/fusion/indexer.py +0 -0
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/src/fusion/ledger.py +0 -0
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/src/fusion/manifest.py +0 -0
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/src/fusion/scaffold.py +0 -0
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/src/fusion/setup.py +0 -0
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/src/fusion/views.py +0 -0
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/tests/__init__.py +0 -0
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/tests/conftest.py +0 -0
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/tests/test_build_payload.py +0 -0
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/tests/test_checker_errors.py +0 -0
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/tests/test_checker_warnings.py +0 -0
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/tests/test_cli.py +0 -0
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/tests/test_cli_encoding.py +0 -0
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/tests/test_cli_setup.py +0 -0
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/tests/test_cli_version.py +0 -0
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/tests/test_document.py +0 -0
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/tests/test_hub_bucket.py +0 -0
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/tests/test_indexer.py +0 -0
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/tests/test_ledger.py +0 -0
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/tests/test_line_endings.py +0 -0
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/tests/test_roundtrip.py +0 -0
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/tests/test_scaffold.py +0 -0
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/tests/test_setup_agents.py +0 -0
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/tests/test_setup_core.py +0 -0
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/tests/test_skill_family.py +0 -0
- {fusion_cli-1.2.1 → fusion_cli-1.3.2}/tests/test_views.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fusion-cli
|
|
3
|
-
Version: 1.2
|
|
3
|
+
Version: 1.3.2
|
|
4
4
|
Summary: The Fusion reference CLI — the notary of the Fusion Convention.
|
|
5
5
|
Author: Bluewaves Boutique
|
|
6
6
|
License-Expression: MIT
|
|
@@ -38,7 +38,7 @@ uv tool install ./fusion/cli
|
|
|
38
38
|
fusion --version
|
|
39
39
|
```
|
|
40
40
|
|
|
41
|
-
## The
|
|
41
|
+
## The ten commands (there is no eleventh)
|
|
42
42
|
|
|
43
43
|
| Command | Does |
|
|
44
44
|
|---|---|
|
|
@@ -51,8 +51,11 @@ fusion --version
|
|
|
51
51
|
| `fusion today` | The composed day, across every bucket in the hub |
|
|
52
52
|
| `fusion agenda` | The wider horizon — dated and active, across the hub |
|
|
53
53
|
| `fusion setup` | Install the skills to `~/.agents/skills` and serve every detected agent (links only where needed) — the installer's brain. `--remove` undoes it |
|
|
54
|
+
| `fusion update` | Bring the whole system current: upgrade fusion-cli through uv, then re-run setup from the new binary so the skills refresh everywhere. Equivalent to `uv tool upgrade fusion-cli && fusion setup` |
|
|
54
55
|
|
|
55
|
-
Every command takes `--json` (agents parse, never scrape)
|
|
56
|
+
Every command takes `--json` (agents parse, never scrape) — except
|
|
57
|
+
`update`, which composes two processes (uv, then setup) that own
|
|
58
|
+
stdout in turn. `log`, `new`,
|
|
56
59
|
and `index` take `--as <actor>`; the pen defaults to `FUSION_ACTOR`, then
|
|
57
60
|
the OS username. `status` and `log` take `--since <date|last-reflection>`.
|
|
58
61
|
`log` also takes `--bucket` (resolves the ledger; default: walk up from the current directory).
|
|
@@ -28,7 +28,7 @@ uv tool install ./fusion/cli
|
|
|
28
28
|
fusion --version
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
-
## The
|
|
31
|
+
## The ten commands (there is no eleventh)
|
|
32
32
|
|
|
33
33
|
| Command | Does |
|
|
34
34
|
|---|---|
|
|
@@ -41,8 +41,11 @@ fusion --version
|
|
|
41
41
|
| `fusion today` | The composed day, across every bucket in the hub |
|
|
42
42
|
| `fusion agenda` | The wider horizon — dated and active, across the hub |
|
|
43
43
|
| `fusion setup` | Install the skills to `~/.agents/skills` and serve every detected agent (links only where needed) — the installer's brain. `--remove` undoes it |
|
|
44
|
+
| `fusion update` | Bring the whole system current: upgrade fusion-cli through uv, then re-run setup from the new binary so the skills refresh everywhere. Equivalent to `uv tool upgrade fusion-cli && fusion setup` |
|
|
44
45
|
|
|
45
|
-
Every command takes `--json` (agents parse, never scrape)
|
|
46
|
+
Every command takes `--json` (agents parse, never scrape) — except
|
|
47
|
+
`update`, which composes two processes (uv, then setup) that own
|
|
48
|
+
stdout in turn. `log`, `new`,
|
|
46
49
|
and `index` take `--as <actor>`; the pen defaults to `FUSION_ACTOR`, then
|
|
47
50
|
the OS username. `status` and `log` take `--since <date|last-reflection>`.
|
|
48
51
|
`log` also takes `--bucket` (resolves the ledger; default: walk up from the current directory).
|
|
@@ -319,6 +319,21 @@ def _render_setup(report: dict) -> list[str]:
|
|
|
319
319
|
return lines
|
|
320
320
|
|
|
321
321
|
|
|
322
|
+
def cmd_update(args) -> int:
|
|
323
|
+
from fusion import update as update_mod
|
|
324
|
+
setup_args = []
|
|
325
|
+
if args.force:
|
|
326
|
+
setup_args.append("--force")
|
|
327
|
+
if args.skills_dir:
|
|
328
|
+
setup_args += ["--skills-dir", args.skills_dir]
|
|
329
|
+
if args.no_agents:
|
|
330
|
+
setup_args.append("--no-agents")
|
|
331
|
+
try:
|
|
332
|
+
return update_mod.run_update(Path.home(), setup_args)
|
|
333
|
+
except update_mod.UpdateError as exc:
|
|
334
|
+
return _fail(str(exc))
|
|
335
|
+
|
|
336
|
+
|
|
322
337
|
# ── parser ───────────────────────────────────────────────────────────────
|
|
323
338
|
|
|
324
339
|
def build_parser() -> argparse.ArgumentParser:
|
|
@@ -418,6 +433,20 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
418
433
|
flag_json(p)
|
|
419
434
|
p.set_defaults(func=cmd_setup)
|
|
420
435
|
|
|
436
|
+
p = sub.add_parser("update", help="upgrade fusion-cli and refresh the skills",
|
|
437
|
+
description="Bring the whole system current: upgrade "
|
|
438
|
+
"fusion-cli through uv, then re-run `fusion setup` "
|
|
439
|
+
"from the new binary so the bundled skills refresh "
|
|
440
|
+
"everywhere setup put them. Equivalent to: "
|
|
441
|
+
"uv tool upgrade fusion-cli && fusion setup.")
|
|
442
|
+
p.add_argument("--force", action="store_true",
|
|
443
|
+
help="forwarded to setup: replace foreign entries")
|
|
444
|
+
p.add_argument("--skills-dir",
|
|
445
|
+
help="forwarded to setup: canonical destination")
|
|
446
|
+
p.add_argument("--no-agents", action="store_true",
|
|
447
|
+
help="forwarded to setup: skip agent fan-out")
|
|
448
|
+
p.set_defaults(func=cmd_update)
|
|
449
|
+
|
|
421
450
|
return parser
|
|
422
451
|
|
|
423
452
|
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"""fusion update — one verb to bring the whole system current.
|
|
2
|
+
|
|
3
|
+
Upgrades fusion-cli through uv, then re-runs `fusion setup` from the
|
|
4
|
+
NEW binary: this process keeps the old skills payload in memory, so
|
|
5
|
+
setup must run in a child for the refreshed payload to land. Every
|
|
6
|
+
decision is testable without a real subprocess — the runner is a seam.
|
|
7
|
+
"""
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import os
|
|
11
|
+
import shutil
|
|
12
|
+
import subprocess
|
|
13
|
+
import sys
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
|
|
16
|
+
from .setup import payload_version
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class UpdateError(Exception):
|
|
20
|
+
"""An update step that cannot proceed."""
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _runner():
|
|
24
|
+
return subprocess.run
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def find_uv(home: Path) -> Path | None:
|
|
28
|
+
found = shutil.which("uv")
|
|
29
|
+
if found:
|
|
30
|
+
return Path(found)
|
|
31
|
+
# install.sh's landing spot, for shells whose PATH never learned it
|
|
32
|
+
for name in ("uv.exe", "uv") if os.name == "nt" else ("uv",):
|
|
33
|
+
candidate = home / ".local" / "bin" / name
|
|
34
|
+
if candidate.is_file() and os.access(candidate, os.X_OK):
|
|
35
|
+
return candidate
|
|
36
|
+
return None
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def uv_owns_install(uv: Path, run) -> bool:
|
|
40
|
+
proc = run([str(uv), "tool", "list"], capture_output=True, text=True)
|
|
41
|
+
if proc.returncode != 0:
|
|
42
|
+
return False
|
|
43
|
+
return any(line.split()[:1] == ["fusion-cli"]
|
|
44
|
+
for line in proc.stdout.splitlines())
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def fusion_binary(uv: Path, run) -> Path:
|
|
48
|
+
proc = run([str(uv), "tool", "dir", "--bin"],
|
|
49
|
+
capture_output=True, text=True)
|
|
50
|
+
if proc.returncode != 0 or not proc.stdout.strip():
|
|
51
|
+
raise UpdateError(
|
|
52
|
+
"could not resolve uv's tool bin dir — finish by hand: "
|
|
53
|
+
"uv tool dir --bin, then <that dir>/fusion setup")
|
|
54
|
+
bin_dir = Path(proc.stdout.strip())
|
|
55
|
+
for name in ("fusion", "fusion.exe"):
|
|
56
|
+
candidate = bin_dir / name
|
|
57
|
+
if candidate.is_file():
|
|
58
|
+
return candidate
|
|
59
|
+
raise UpdateError(
|
|
60
|
+
f"fusion not found in {bin_dir} after the upgrade — "
|
|
61
|
+
f"check: uv tool list")
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def run_update(home: Path, setup_args: list[str]) -> int:
|
|
65
|
+
run = _runner()
|
|
66
|
+
uv = find_uv(home)
|
|
67
|
+
if uv is None:
|
|
68
|
+
raise UpdateError(
|
|
69
|
+
"uv not found — fusion updates through uv "
|
|
70
|
+
"(https://docs.astral.sh/uv/). Install it, or upgrade "
|
|
71
|
+
"fusion-cli with whatever installed it, then run: fusion setup")
|
|
72
|
+
if not uv_owns_install(uv, run):
|
|
73
|
+
raise UpdateError(
|
|
74
|
+
"this fusion-cli is not managed by uv — upgrade it with "
|
|
75
|
+
"whatever installed it (pip, pipx, a checkout), then run: "
|
|
76
|
+
"fusion setup")
|
|
77
|
+
print(f"fusion-cli {payload_version()} — asking uv for newer…")
|
|
78
|
+
sys.stdout.flush() # keep our line ahead of uv's inherited stdio
|
|
79
|
+
if run([str(uv), "tool", "upgrade", "fusion-cli"]).returncode != 0:
|
|
80
|
+
raise UpdateError(
|
|
81
|
+
"uv tool upgrade failed — on Windows the running fusion.exe "
|
|
82
|
+
"can hold the lock. Exit this process, then run: "
|
|
83
|
+
"uv tool upgrade fusion-cli && fusion setup")
|
|
84
|
+
child = run([str(fusion_binary(uv, run)), "setup", *setup_args])
|
|
85
|
+
return child.returncode
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"""fusion update end to end through the CLI surface.
|
|
2
|
+
|
|
3
|
+
Same isolation contract as test_cli_setup.py: no real subprocess is
|
|
4
|
+
ever reached; a fake runner records the exact command sequence.
|
|
5
|
+
"""
|
|
6
|
+
import subprocess
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from types import SimpleNamespace
|
|
9
|
+
|
|
10
|
+
import pytest
|
|
11
|
+
|
|
12
|
+
from fusion import update
|
|
13
|
+
from fusion.cli import build_parser, main
|
|
14
|
+
|
|
15
|
+
# find_uv wraps which()'s answer in Path, so the argv the runner sees is
|
|
16
|
+
# the platform's rendering of this stub ("\\stub\\uv" on Windows).
|
|
17
|
+
STUB_UV = str(Path("/stub/uv"))
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class FakeRunner:
|
|
21
|
+
"""Answers uv/fusion invocations from a script of canned results."""
|
|
22
|
+
|
|
23
|
+
def __init__(self, bin_dir: Path, *, list_stdout="fusion-cli v1.2.1\n- fusion\n",
|
|
24
|
+
list_rc=0, upgrade_rc=0, setup_rc=0):
|
|
25
|
+
self.bin_dir = bin_dir
|
|
26
|
+
self.list_stdout = list_stdout
|
|
27
|
+
self.list_rc = list_rc
|
|
28
|
+
self.upgrade_rc = upgrade_rc
|
|
29
|
+
self.setup_rc = setup_rc
|
|
30
|
+
self.calls = []
|
|
31
|
+
|
|
32
|
+
def __call__(self, argv, **kwargs):
|
|
33
|
+
self.calls.append([str(a) for a in argv])
|
|
34
|
+
prog, rest = Path(argv[0]).name, [str(a) for a in argv[1:]]
|
|
35
|
+
if prog.startswith("uv") and rest == ["tool", "list"]:
|
|
36
|
+
return SimpleNamespace(returncode=self.list_rc,
|
|
37
|
+
stdout=self.list_stdout, stderr="")
|
|
38
|
+
if prog.startswith("uv") and rest == ["tool", "upgrade", "fusion-cli"]:
|
|
39
|
+
return SimpleNamespace(returncode=self.upgrade_rc, stdout="", stderr="")
|
|
40
|
+
if prog.startswith("uv") and rest == ["tool", "dir", "--bin"]:
|
|
41
|
+
return SimpleNamespace(returncode=0, stdout=f"{self.bin_dir}\n", stderr="")
|
|
42
|
+
if prog.startswith("fusion") and rest[:1] == ["setup"]:
|
|
43
|
+
return SimpleNamespace(returncode=self.setup_rc, stdout="", stderr="")
|
|
44
|
+
raise AssertionError(f"unexpected command: {argv}")
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@pytest.fixture
|
|
48
|
+
def sandbox(tmp_path, monkeypatch):
|
|
49
|
+
home = tmp_path / "home"
|
|
50
|
+
home.mkdir()
|
|
51
|
+
monkeypatch.setenv("HOME", str(home))
|
|
52
|
+
monkeypatch.setenv("USERPROFILE", str(home)) # windows
|
|
53
|
+
monkeypatch.setattr(Path, "home", classmethod(lambda cls: home))
|
|
54
|
+
monkeypatch.setattr(update.shutil, "which",
|
|
55
|
+
lambda name, *a, **kw: "/stub/uv" if name == "uv" else None)
|
|
56
|
+
monkeypatch.setattr(subprocess, "run",
|
|
57
|
+
lambda *a, **kw: pytest.fail("real subprocess reached"))
|
|
58
|
+
bin_dir = tmp_path / "toolbin"
|
|
59
|
+
bin_dir.mkdir()
|
|
60
|
+
(bin_dir / "fusion").write_text("")
|
|
61
|
+
return SimpleNamespace(home=home, bin_dir=bin_dir)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _install_runner(monkeypatch, runner):
|
|
65
|
+
monkeypatch.setattr(update, "_runner", lambda: runner)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def test_update_happy_path_runs_upgrade_then_setup(sandbox, monkeypatch, capsys):
|
|
69
|
+
runner = FakeRunner(sandbox.bin_dir)
|
|
70
|
+
_install_runner(monkeypatch, runner)
|
|
71
|
+
assert main(["update"]) == 0
|
|
72
|
+
assert runner.calls == [
|
|
73
|
+
[STUB_UV, "tool", "list"],
|
|
74
|
+
[STUB_UV, "tool", "upgrade", "fusion-cli"],
|
|
75
|
+
[STUB_UV, "tool", "dir", "--bin"],
|
|
76
|
+
[str(sandbox.bin_dir / "fusion"), "setup"],
|
|
77
|
+
]
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def test_update_forwards_setup_flags(sandbox, monkeypatch):
|
|
81
|
+
runner = FakeRunner(sandbox.bin_dir)
|
|
82
|
+
_install_runner(monkeypatch, runner)
|
|
83
|
+
custom = str(sandbox.home / "custom-skills")
|
|
84
|
+
assert main(["update", "--force", "--skills-dir", custom,
|
|
85
|
+
"--no-agents"]) == 0
|
|
86
|
+
assert runner.calls[-1] == [str(sandbox.bin_dir / "fusion"), "setup",
|
|
87
|
+
"--force", "--skills-dir", custom,
|
|
88
|
+
"--no-agents"]
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def test_update_propagates_setup_exit_code(sandbox, monkeypatch):
|
|
92
|
+
runner = FakeRunner(sandbox.bin_dir, setup_rc=3)
|
|
93
|
+
_install_runner(monkeypatch, runner)
|
|
94
|
+
assert main(["update"]) == 3
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def test_update_without_uv_fails_with_manual_path(sandbox, monkeypatch, capsys):
|
|
98
|
+
monkeypatch.setattr(update.shutil, "which", lambda name, *a, **kw: None)
|
|
99
|
+
_install_runner(monkeypatch, FakeRunner(sandbox.bin_dir))
|
|
100
|
+
assert main(["update"]) == 1
|
|
101
|
+
err = capsys.readouterr().err
|
|
102
|
+
assert "uv" in err and "fusion setup" in err
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def test_update_finds_uv_at_install_sh_fallback(sandbox, monkeypatch):
|
|
106
|
+
monkeypatch.setattr(update.shutil, "which", lambda name, *a, **kw: None)
|
|
107
|
+
fallback = sandbox.home / ".local" / "bin" / "uv"
|
|
108
|
+
fallback.parent.mkdir(parents=True)
|
|
109
|
+
fallback.write_text("")
|
|
110
|
+
fallback.chmod(0o755)
|
|
111
|
+
runner = FakeRunner(sandbox.bin_dir)
|
|
112
|
+
_install_runner(monkeypatch, runner)
|
|
113
|
+
assert main(["update"]) == 0
|
|
114
|
+
assert runner.calls[0][0] == str(fallback)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def test_update_refuses_non_uv_install(sandbox, monkeypatch, capsys):
|
|
118
|
+
runner = FakeRunner(sandbox.bin_dir, list_stdout="black v24.0.0\n- black\n")
|
|
119
|
+
_install_runner(monkeypatch, runner)
|
|
120
|
+
assert main(["update"]) == 1
|
|
121
|
+
err = capsys.readouterr().err
|
|
122
|
+
assert "not managed by uv" in err and "fusion setup" in err
|
|
123
|
+
# never got as far as upgrading
|
|
124
|
+
assert [STUB_UV, "tool", "upgrade", "fusion-cli"] not in runner.calls
|
|
125
|
+
assert runner.calls == [[STUB_UV, "tool", "list"]]
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def test_update_reports_failed_upgrade_with_retry_ritual(sandbox, monkeypatch,
|
|
129
|
+
capsys):
|
|
130
|
+
runner = FakeRunner(sandbox.bin_dir, upgrade_rc=1)
|
|
131
|
+
_install_runner(monkeypatch, runner)
|
|
132
|
+
assert main(["update"]) == 1
|
|
133
|
+
err = capsys.readouterr().err
|
|
134
|
+
assert "uv tool upgrade fusion-cli && fusion setup" in err
|
|
135
|
+
# setup never ran on a failed upgrade
|
|
136
|
+
assert all(c[1:2] != ["setup"] for c in runner.calls)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def test_update_appears_in_help(sandbox):
|
|
140
|
+
assert "update" in build_parser().format_help()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fusion_cli-1.2.1 → fusion_cli-1.3.2}/skills/fusion-analyst/references/fusion-conventions.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fusion_cli-1.2.1 → fusion_cli-1.3.2}/skills/fusion-librarian/references/fusion-conventions.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fusion_cli-1.2.1 → fusion_cli-1.3.2}/skills/fusion-planner/references/fusion-conventions.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|