hill 0.2.0__py3-none-any.whl
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.
- hill/__init__.py +164 -0
- hill/__main__.py +3 -0
- hill/app.py +2983 -0
- hill/calendar.py +141 -0
- hill/claude.py +525 -0
- hill/config.py +111 -0
- hill/hill.toml +125 -0
- hill/mod/.claude-plugin/plugin.json +9 -0
- hill/mod/.gitignore +1 -0
- hill/mod/hooks/guard.test.ts +36 -0
- hill/mod/hooks/guard.ts +96 -0
- hill/mod/hooks/hooks.json +1 -0
- hill/mod/hooks/parse.test.ts +33 -0
- hill/mod/hooks/parse.ts +22 -0
- hill/mod/hooks/register.tsx +130 -0
- hill/mod/tsconfig.json +3 -0
- hill/mod/types/index.d.ts +14 -0
- hill/reference.py +41 -0
- hill/restart.py +92 -0
- hill/side.py +457 -0
- hill/starter/Home.md +14 -0
- hill/starter/Keys.md +18 -0
- hill/starter/garden/README.md +17 -0
- hill/starter/garden/work/001-raised-beds-sample.md +30 -0
- hill/starter/garden/work/README.md +8 -0
- hill/sync.py +206 -0
- hill-0.2.0.dist-info/METADATA +12 -0
- hill-0.2.0.dist-info/RECORD +30 -0
- hill-0.2.0.dist-info/WHEEL +4 -0
- hill-0.2.0.dist-info/entry_points.txt +5 -0
hill/__init__.py
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
"""palace: a notes screen for a Markdown vault, with micro for editing. Its
|
|
2
|
+
settings are in hill-ops's strip, under it."""
|
|
3
|
+
|
|
4
|
+
import os
|
|
5
|
+
import sys
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
USAGE = """\
|
|
9
|
+
usage: hill [FOLDER] the notes screen, for FOLDER, else the folder
|
|
10
|
+
chosen on the first start
|
|
11
|
+
hill --resume [FOLDER] pick which of hill-ops's instances to start in
|
|
12
|
+
hill --new [FOLDER] in a new instance
|
|
13
|
+
|
|
14
|
+
palace is published as hill, and runs as `hill` or `palace`.
|
|
15
|
+
|
|
16
|
+
palace starts itself inside hill-ops, whose strip under it holds its settings,
|
|
17
|
+
(hill-ops, which it brings along), when it runs in a terminal;
|
|
18
|
+
PALACE_NO_HILL=1 keeps it on its own. hill-ops keeps an instance for each
|
|
19
|
+
palace, with its place, its panes and their widths, and starts the last one
|
|
20
|
+
that isn't running unless told otherwise. `hill-ops settings` shows the
|
|
21
|
+
settings on their own.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
HILL_OPS = [sys.executable, "-m", "hill_ops"]
|
|
26
|
+
"""hill-ops's command, from palace's own environment, where palace's
|
|
27
|
+
package brings it: `uv tool install` puts only palace's commands on PATH."""
|
|
28
|
+
|
|
29
|
+
LOOP = [str(Path(sys.executable).with_name("palace")), "_loop"]
|
|
30
|
+
"""palace-loop, as palace starts it inside hill-ops: through its `palace`
|
|
31
|
+
command, beside the Python that runs it, so that hill-ops names the
|
|
32
|
+
program palace, as its instances and event log did when the import was
|
|
33
|
+
palace (work item 018 in hill-ops)."""
|
|
34
|
+
|
|
35
|
+
AGAIN = "PALACE_HILL_INSTANCE"
|
|
36
|
+
"""A file, set by the palace that starts hill-ops, where the palace inside
|
|
37
|
+
writes hill-ops's instance, to start hill-ops again in the same one."""
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def into_hill(args: list[str], choice: str | None = None) -> None:
|
|
41
|
+
"""Start palace again inside hill-ops, under palace-loop, unless it's there
|
|
42
|
+
already, it isn't in a terminal, hill-ops isn't installed, or PALACE_NO_HILL
|
|
43
|
+
is set; and start hill-ops again, in the same instance, each time palace
|
|
44
|
+
asks to restart it all. `choice` ("resume" or "new") says which
|
|
45
|
+
instance to start in."""
|
|
46
|
+
if os.environ.get("HILL_SOCKET"):
|
|
47
|
+
if (note := os.environ.get(AGAIN)) and (instance := os.environ.get("HILL_INSTANCE")):
|
|
48
|
+
try:
|
|
49
|
+
Path(note).write_text(Path(instance).name)
|
|
50
|
+
except OSError:
|
|
51
|
+
pass
|
|
52
|
+
return
|
|
53
|
+
if os.environ.get("PALACE_NO_HILL"):
|
|
54
|
+
return
|
|
55
|
+
if not (sys.stdin.isatty() and sys.stdout.isatty()):
|
|
56
|
+
return
|
|
57
|
+
if _hill_installed():
|
|
58
|
+
import subprocess
|
|
59
|
+
import tempfile
|
|
60
|
+
|
|
61
|
+
from .restart import RESTART_ALL
|
|
62
|
+
|
|
63
|
+
handle, name = tempfile.mkstemp(prefix="palace-instance-")
|
|
64
|
+
os.close(handle)
|
|
65
|
+
env = os.environ | {AGAIN: name}
|
|
66
|
+
flags = [f"--{choice}"] if choice else []
|
|
67
|
+
try:
|
|
68
|
+
while (status := subprocess.call([*HILL_OPS, *flags, "--", *LOOP, *args], env=env)) == RESTART_ALL:
|
|
69
|
+
instance = Path(name).read_text().strip()
|
|
70
|
+
flags = ["--as", instance] if instance else []
|
|
71
|
+
finally:
|
|
72
|
+
Path(name).unlink(missing_ok=True)
|
|
73
|
+
sys.exit(status)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _hill_installed() -> bool:
|
|
77
|
+
import importlib.util
|
|
78
|
+
|
|
79
|
+
return importlib.util.find_spec("hill_ops") is not None
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _report_to_hill(errors: list[str]) -> None:
|
|
83
|
+
"""Tell hill-ops's event log what went wrong in the sync. It runs once
|
|
84
|
+
palace has left hill-ops's channel, so palace says hello again to do so."""
|
|
85
|
+
import asyncio
|
|
86
|
+
|
|
87
|
+
from hill_client import SOCKET, connect, line
|
|
88
|
+
|
|
89
|
+
if not (path := os.environ.get(SOCKET)):
|
|
90
|
+
return
|
|
91
|
+
|
|
92
|
+
async def tell() -> None:
|
|
93
|
+
_, writer = await connect(path, wait=0)
|
|
94
|
+
writer.write(line("hello", app="palace") + b"".join(line("error", what=what) for what in errors))
|
|
95
|
+
await writer.drain()
|
|
96
|
+
writer.close()
|
|
97
|
+
await writer.wait_closed()
|
|
98
|
+
|
|
99
|
+
try:
|
|
100
|
+
asyncio.run(tell())
|
|
101
|
+
except OSError:
|
|
102
|
+
pass # hill-ops has gone: nothing to tell
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def main() -> None:
|
|
106
|
+
args = sys.argv[1:]
|
|
107
|
+
if args in (["-h"], ["--help"]):
|
|
108
|
+
print(USAGE, end="")
|
|
109
|
+
elif args[:1] == ["_loop"] and len(args) <= 2:
|
|
110
|
+
# palace-loop, as hill-ops runs it (see restart.py).
|
|
111
|
+
from .restart import loop
|
|
112
|
+
|
|
113
|
+
loop(args[1:])
|
|
114
|
+
elif len(args) == 2 and args[0] == "_preview":
|
|
115
|
+
# The preview's pane, beside palace inside hill-ops (see side.py).
|
|
116
|
+
from .side import main as side
|
|
117
|
+
|
|
118
|
+
side("preview", args[1])
|
|
119
|
+
elif len(args) >= 5 and args[0] == "_claude":
|
|
120
|
+
# A Claude session's pane, beside palace inside hill-ops (see claude.py).
|
|
121
|
+
from .claude import run_pane
|
|
122
|
+
|
|
123
|
+
done = args[4] == "--done"
|
|
124
|
+
mirror = args[5] if args[4] == "--mirror" and len(args) > 5 else None
|
|
125
|
+
sys.exit(run_pane(args[1], args[2], args[3], args[4 + done + 2 * bool(mirror):], done=done, mirror=mirror))
|
|
126
|
+
elif args == ["_hook"]:
|
|
127
|
+
# A Claude Code hook, in a session palace started (see claude.py).
|
|
128
|
+
from .claude import hook
|
|
129
|
+
|
|
130
|
+
hook()
|
|
131
|
+
elif len(args) <= 1 or (args[0] in ("--resume", "--new") and len(args) <= 2):
|
|
132
|
+
choice = args[0][2:] if args[:1] in (["--resume"], ["--new"]) else None
|
|
133
|
+
args = args[1:] if choice else args
|
|
134
|
+
folder = Path(args[0]).expanduser() if args else None
|
|
135
|
+
if folder is not None and not folder.is_dir():
|
|
136
|
+
print(f"palace: {folder} isn't a folder", file=sys.stderr)
|
|
137
|
+
sys.exit(2)
|
|
138
|
+
if folder is None:
|
|
139
|
+
from .config import ask_folder
|
|
140
|
+
|
|
141
|
+
ask_folder()
|
|
142
|
+
into_hill(args, choice)
|
|
143
|
+
from .app import PalaceApp
|
|
144
|
+
from .restart import RESTART, RESTART_ALL, again
|
|
145
|
+
from .sync import sync_vault
|
|
146
|
+
|
|
147
|
+
app = PalaceApp(folder)
|
|
148
|
+
app.run()
|
|
149
|
+
if app.return_code in (RESTART, RESTART_ALL):
|
|
150
|
+
again(app.return_code) # the sync waits for the last quit
|
|
151
|
+
errors: list[str] = []
|
|
152
|
+
report = sync_vault(
|
|
153
|
+
app.vault.root,
|
|
154
|
+
commit=app.pref(("sync", "commit"), True),
|
|
155
|
+
push=app.pref(("sync", "push"), True),
|
|
156
|
+
failed=errors.append,
|
|
157
|
+
)
|
|
158
|
+
if report:
|
|
159
|
+
print(report)
|
|
160
|
+
if errors:
|
|
161
|
+
_report_to_hill(errors)
|
|
162
|
+
else:
|
|
163
|
+
print(USAGE, end="", file=sys.stderr)
|
|
164
|
+
sys.exit(2)
|
hill/__main__.py
ADDED