arctx-cli 0.2.0b2__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.
- arctx_cli/__init__.py +1 -0
- arctx_cli/alias.py +238 -0
- arctx_cli/append_batch.py +90 -0
- arctx_cli/commands/__init__.py +85 -0
- arctx_cli/commands/alias_cmd.py +174 -0
- arctx_cli/commands/anchor.py +82 -0
- arctx_cli/commands/current.py +69 -0
- arctx_cli/commands/cut.py +89 -0
- arctx_cli/commands/dump.py +72 -0
- arctx_cli/commands/ext.py +236 -0
- arctx_cli/commands/git.py +216 -0
- arctx_cli/commands/graph.py +73 -0
- arctx_cli/commands/guide.py +360 -0
- arctx_cli/commands/init.py +223 -0
- arctx_cli/commands/list.py +45 -0
- arctx_cli/commands/migrate.py +135 -0
- arctx_cli/commands/node.py +55 -0
- arctx_cli/commands/outcomes.py +58 -0
- arctx_cli/commands/payload.py +192 -0
- arctx_cli/commands/reachable.py +75 -0
- arctx_cli/commands/show.py +113 -0
- arctx_cli/commands/sync.py +244 -0
- arctx_cli/commands/trace.py +46 -0
- arctx_cli/commands/transition.py +212 -0
- arctx_cli/commands/use.py +67 -0
- arctx_cli/commands/view.py +82 -0
- arctx_cli/commands/work_session.py +330 -0
- arctx_cli/context.py +38 -0
- arctx_cli/ext/__init__.py +1 -0
- arctx_cli/ext/command/__init__.py +110 -0
- arctx_cli/ext/git/__init__.py +1 -0
- arctx_cli/ext/git/branch.py +140 -0
- arctx_cli/ext/git/cherry_pick.py +144 -0
- arctx_cli/ext/git/commit.py +205 -0
- arctx_cli/ext/git/hook.py +758 -0
- arctx_cli/ext/git/merge.py +204 -0
- arctx_cli/ext/git/reset.py +138 -0
- arctx_cli/ext/git/revert.py +157 -0
- arctx_cli/ext/git/verify.py +140 -0
- arctx_cli/ext/git/worktree.py +173 -0
- arctx_cli/ext_registry.py +34 -0
- arctx_cli/main.py +133 -0
- arctx_cli/paths.py +27 -0
- arctx_cli/payload_builder.py +23 -0
- arctx_cli/workspace.py +64 -0
- arctx_cli-0.2.0b2.dist-info/METADATA +48 -0
- arctx_cli-0.2.0b2.dist-info/RECORD +49 -0
- arctx_cli-0.2.0b2.dist-info/WHEEL +4 -0
- arctx_cli-0.2.0b2.dist-info/entry_points.txt +2 -0
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
"""arctx git worktree — thin wrappers around ``git worktree``.
|
|
2
|
+
|
|
3
|
+
This is a lifecycle helper. The actual graph state lives on the
|
|
4
|
+
WorkSession that is later attached to the worktree via
|
|
5
|
+
``arctx work-session env --worktree`` (or via spawn/start). These
|
|
6
|
+
commands only manage the git side: ``add`` creates a new
|
|
7
|
+
``git worktree`` on a fresh branch, ``list`` shows the registered
|
|
8
|
+
worktrees, and ``remove`` invokes ``git worktree remove``.
|
|
9
|
+
|
|
10
|
+
Recording per-worktree session metadata is intentionally left to
|
|
11
|
+
``arctx work-session start --worktree``; that keeps a single canonical
|
|
12
|
+
path for writing WorkSession state and lets users attach a worktree
|
|
13
|
+
that was created outside arctx (``git worktree add`` directly).
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import argparse
|
|
19
|
+
import json
|
|
20
|
+
import subprocess
|
|
21
|
+
import sys
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
|
|
24
|
+
from arctx.ext.git.helpers.repo import resolve_worktree_path
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def add_parser(subparsers) -> argparse.ArgumentParser:
|
|
28
|
+
"""Register the ``worktree`` subcommand under ``arctx git``."""
|
|
29
|
+
parser = subparsers.add_parser(
|
|
30
|
+
"worktree",
|
|
31
|
+
help="Manage git worktrees attached to this ARCTX run",
|
|
32
|
+
description=(
|
|
33
|
+
"Thin wrapper around `git worktree`. Use these to create / list "
|
|
34
|
+
"/ remove worktrees; attach them to a ARCTX work session with "
|
|
35
|
+
"`arctx work-session start --worktree PATH`."
|
|
36
|
+
),
|
|
37
|
+
)
|
|
38
|
+
sub = parser.add_subparsers(dest="worktree_command", required=True)
|
|
39
|
+
|
|
40
|
+
add = sub.add_parser(
|
|
41
|
+
"add",
|
|
42
|
+
help="git worktree add PATH [BRANCH] — create a new worktree",
|
|
43
|
+
)
|
|
44
|
+
add.add_argument("path", help="Filesystem path for the new worktree")
|
|
45
|
+
add.add_argument(
|
|
46
|
+
"branch",
|
|
47
|
+
nargs="?",
|
|
48
|
+
default=None,
|
|
49
|
+
help="Branch to check out. Defaults to a new branch named after the path leaf.",
|
|
50
|
+
)
|
|
51
|
+
add.add_argument(
|
|
52
|
+
"--base",
|
|
53
|
+
default=None,
|
|
54
|
+
help="Base ref for the new branch (defaults to HEAD).",
|
|
55
|
+
)
|
|
56
|
+
add.add_argument(
|
|
57
|
+
"--existing-branch",
|
|
58
|
+
action="store_true",
|
|
59
|
+
help="Check out an existing branch instead of creating one.",
|
|
60
|
+
)
|
|
61
|
+
add.add_argument("--store-dir", default=None)
|
|
62
|
+
|
|
63
|
+
list_cmd = sub.add_parser("list", help="git worktree list --porcelain (parsed as JSON)")
|
|
64
|
+
list_cmd.add_argument("--store-dir", default=None)
|
|
65
|
+
|
|
66
|
+
remove = sub.add_parser("remove", help="git worktree remove PATH")
|
|
67
|
+
remove.add_argument("path")
|
|
68
|
+
remove.add_argument(
|
|
69
|
+
"--force",
|
|
70
|
+
action="store_true",
|
|
71
|
+
help="Pass --force to git worktree remove (drop dirty / locked).",
|
|
72
|
+
)
|
|
73
|
+
remove.add_argument("--store-dir", default=None)
|
|
74
|
+
|
|
75
|
+
return parser
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def cli_worktree(args) -> int:
|
|
79
|
+
"""Dispatch ``arctx git worktree`` subcommands."""
|
|
80
|
+
if args.worktree_command == "add":
|
|
81
|
+
return _cli_worktree_add(args)
|
|
82
|
+
if args.worktree_command == "list":
|
|
83
|
+
return _cli_worktree_list(args)
|
|
84
|
+
if args.worktree_command == "remove":
|
|
85
|
+
return _cli_worktree_remove(args)
|
|
86
|
+
print(f"unknown worktree subcommand: {args.worktree_command}", file=sys.stderr)
|
|
87
|
+
return 1
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _cli_worktree_add(args) -> int:
|
|
91
|
+
target = Path(args.path).expanduser().resolve()
|
|
92
|
+
cwd = resolve_worktree_path(None)
|
|
93
|
+
cmd = ["git", "worktree", "add"]
|
|
94
|
+
if args.branch is None and not args.existing_branch:
|
|
95
|
+
leaf = target.name or "arctx-worktree"
|
|
96
|
+
cmd += ["-b", leaf, str(target)]
|
|
97
|
+
if args.base:
|
|
98
|
+
cmd.append(args.base)
|
|
99
|
+
elif args.existing_branch:
|
|
100
|
+
if args.branch is None:
|
|
101
|
+
print("error: --existing-branch requires BRANCH", file=sys.stderr)
|
|
102
|
+
return 2
|
|
103
|
+
cmd += [str(target), args.branch]
|
|
104
|
+
else:
|
|
105
|
+
# explicit new branch name
|
|
106
|
+
cmd += ["-b", args.branch, str(target)]
|
|
107
|
+
if args.base:
|
|
108
|
+
cmd.append(args.base)
|
|
109
|
+
|
|
110
|
+
result = subprocess.run(cmd, cwd=str(cwd), capture_output=True, text=True)
|
|
111
|
+
if result.returncode != 0:
|
|
112
|
+
sys.stderr.write(result.stderr)
|
|
113
|
+
return result.returncode
|
|
114
|
+
|
|
115
|
+
print(
|
|
116
|
+
json.dumps(
|
|
117
|
+
{
|
|
118
|
+
"path": str(target),
|
|
119
|
+
"branch": args.branch
|
|
120
|
+
or (target.name or "arctx-worktree" if not args.existing_branch else None),
|
|
121
|
+
"command": cmd,
|
|
122
|
+
},
|
|
123
|
+
ensure_ascii=False,
|
|
124
|
+
indent=2,
|
|
125
|
+
)
|
|
126
|
+
)
|
|
127
|
+
return 0
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def _cli_worktree_list(args) -> int: # noqa: ARG001
|
|
131
|
+
cwd = resolve_worktree_path(None)
|
|
132
|
+
result = subprocess.run(
|
|
133
|
+
["git", "worktree", "list", "--porcelain"],
|
|
134
|
+
cwd=str(cwd),
|
|
135
|
+
capture_output=True,
|
|
136
|
+
text=True,
|
|
137
|
+
)
|
|
138
|
+
if result.returncode != 0:
|
|
139
|
+
sys.stderr.write(result.stderr)
|
|
140
|
+
return result.returncode
|
|
141
|
+
|
|
142
|
+
entries: list[dict] = []
|
|
143
|
+
current: dict = {}
|
|
144
|
+
for line in result.stdout.splitlines():
|
|
145
|
+
if not line.strip():
|
|
146
|
+
if current:
|
|
147
|
+
entries.append(current)
|
|
148
|
+
current = {}
|
|
149
|
+
continue
|
|
150
|
+
if " " in line:
|
|
151
|
+
key, value = line.split(" ", 1)
|
|
152
|
+
else:
|
|
153
|
+
key, value = line, ""
|
|
154
|
+
current[key] = value
|
|
155
|
+
if current:
|
|
156
|
+
entries.append(current)
|
|
157
|
+
|
|
158
|
+
print(json.dumps({"worktrees": entries}, ensure_ascii=False, indent=2))
|
|
159
|
+
return 0
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def _cli_worktree_remove(args) -> int:
|
|
163
|
+
cwd = resolve_worktree_path(None)
|
|
164
|
+
cmd = ["git", "worktree", "remove"]
|
|
165
|
+
if args.force:
|
|
166
|
+
cmd.append("--force")
|
|
167
|
+
cmd.append(str(Path(args.path).expanduser()))
|
|
168
|
+
result = subprocess.run(cmd, cwd=str(cwd), capture_output=True, text=True)
|
|
169
|
+
if result.returncode != 0:
|
|
170
|
+
sys.stderr.write(result.stderr)
|
|
171
|
+
return result.returncode
|
|
172
|
+
print(json.dumps({"removed": str(Path(args.path).expanduser().resolve())}, indent=2))
|
|
173
|
+
return 0
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""CLI-side extension registration.
|
|
2
|
+
|
|
3
|
+
Keeps the arctx layer free of CLI dependencies. ``arctx_cli.main``
|
|
4
|
+
calls :func:`register_enabled_cli` to attach argparse subparsers for each
|
|
5
|
+
extension that is enabled in the current run directory.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Iterable
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def register_extension_cli(subparsers, names: Iterable[str]) -> None:
|
|
15
|
+
"""Register CLI namespaces provided by the named extensions."""
|
|
16
|
+
from arctx.ext import load_extension
|
|
17
|
+
from arctx_cli.commands import register_cli_commands
|
|
18
|
+
|
|
19
|
+
seen: set[str] = set()
|
|
20
|
+
for name in names:
|
|
21
|
+
if name in seen:
|
|
22
|
+
continue
|
|
23
|
+
ext = load_extension(name)
|
|
24
|
+
register_cli_commands(subparsers, ext.cli_commands())
|
|
25
|
+
seen.add(ext.name)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def register_enabled_cli(subparsers, run_dir: str | Path | None) -> None:
|
|
29
|
+
"""Register CLI namespaces for extensions enabled in a run directory."""
|
|
30
|
+
if run_dir is None:
|
|
31
|
+
return
|
|
32
|
+
from arctx.ext.enabled import load_enabled
|
|
33
|
+
|
|
34
|
+
register_extension_cli(subparsers, (item.name for item in load_enabled(run_dir)))
|
arctx_cli/main.py
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"""arctx CLI entry point."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import sys
|
|
7
|
+
|
|
8
|
+
from arctx_cli.commands import core_cli_commands, register_cli_commands
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _build_parser(*, run_dir: str | None = None) -> argparse.ArgumentParser:
|
|
12
|
+
parser = argparse.ArgumentParser(
|
|
13
|
+
prog="arctx",
|
|
14
|
+
description="Record optimization and problem-solving processes",
|
|
15
|
+
)
|
|
16
|
+
subparsers = parser.add_subparsers(dest="command", required=True)
|
|
17
|
+
|
|
18
|
+
register_cli_commands(subparsers, core_cli_commands())
|
|
19
|
+
|
|
20
|
+
from arctx_cli.ext_registry import register_enabled_cli # noqa: PLC0415
|
|
21
|
+
|
|
22
|
+
register_enabled_cli(subparsers, run_dir)
|
|
23
|
+
|
|
24
|
+
return parser
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _resolve_run_dir_for_alias(tokens: list[str]) -> str | None:
|
|
28
|
+
"""Best-effort resolution of run_dir for alias loading.
|
|
29
|
+
|
|
30
|
+
Reads ``--run`` / ``ARCTX_RUN_ID`` / ``<gitdir>/arctx-id``. Returns None if no
|
|
31
|
+
run can be resolved without side-effects.
|
|
32
|
+
"""
|
|
33
|
+
import os
|
|
34
|
+
from pathlib import Path
|
|
35
|
+
|
|
36
|
+
# Look for --run <id> in tokens
|
|
37
|
+
run_id: str | None = None
|
|
38
|
+
store_dir: str | None = None
|
|
39
|
+
for i, tok in enumerate(tokens):
|
|
40
|
+
if tok == "--run" and i + 1 < len(tokens):
|
|
41
|
+
run_id = tokens[i + 1]
|
|
42
|
+
if tok == "--store-dir" and i + 1 < len(tokens):
|
|
43
|
+
store_dir = tokens[i + 1]
|
|
44
|
+
if tok.startswith("--run="):
|
|
45
|
+
run_id = tok[6:]
|
|
46
|
+
if tok.startswith("--store-dir="):
|
|
47
|
+
store_dir = tok[12:]
|
|
48
|
+
|
|
49
|
+
if run_id is None:
|
|
50
|
+
run_id = os.environ.get("ARCTX_RUN_ID")
|
|
51
|
+
|
|
52
|
+
if run_id is None:
|
|
53
|
+
# Try <gitdir>/arctx-id
|
|
54
|
+
try:
|
|
55
|
+
from arctx_cli.paths import find_repo_root, read_arctx_id # noqa: PLC0415
|
|
56
|
+
|
|
57
|
+
repo_root = find_repo_root()
|
|
58
|
+
run_id = read_arctx_id(repo_root)
|
|
59
|
+
except Exception: # noqa: BLE001
|
|
60
|
+
pass
|
|
61
|
+
|
|
62
|
+
if run_id is None:
|
|
63
|
+
return None
|
|
64
|
+
|
|
65
|
+
if store_dir is None:
|
|
66
|
+
try:
|
|
67
|
+
from arctx_cli.paths import resolve_store_dir # noqa: PLC0415
|
|
68
|
+
|
|
69
|
+
store_dir = resolve_store_dir()
|
|
70
|
+
except Exception: # noqa: BLE001
|
|
71
|
+
return None
|
|
72
|
+
|
|
73
|
+
candidate = Path(store_dir) / run_id
|
|
74
|
+
return str(candidate) if candidate.is_dir() else None
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _collect_ext_default_aliases(run_dir: str | None) -> list[dict[str, str]]:
|
|
78
|
+
"""Load default_aliases from extensions enabled in the current run."""
|
|
79
|
+
from arctx.ext import load_extension # noqa: PLC0415
|
|
80
|
+
from arctx.ext.enabled import load_enabled # noqa: PLC0415
|
|
81
|
+
|
|
82
|
+
ext_aliases: list[dict[str, str]] = []
|
|
83
|
+
seen: set[str] = set()
|
|
84
|
+
if run_dir is None:
|
|
85
|
+
return ext_aliases
|
|
86
|
+
|
|
87
|
+
for ee in load_enabled(run_dir):
|
|
88
|
+
if ee.name in seen:
|
|
89
|
+
continue
|
|
90
|
+
try:
|
|
91
|
+
ext = load_extension(ee.name)
|
|
92
|
+
ext_aliases.append(ext.default_aliases())
|
|
93
|
+
seen.add(ext.name)
|
|
94
|
+
except (KeyError, ImportError):
|
|
95
|
+
continue
|
|
96
|
+
return ext_aliases
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def parse_args(argv: list[str] | None = None):
|
|
100
|
+
"""Parse CLI arguments."""
|
|
101
|
+
tokens: list[str] | None = None if argv is None else list(argv)
|
|
102
|
+
run_dir = _resolve_run_dir_for_alias(tokens or sys.argv[1:])
|
|
103
|
+
parser = _build_parser(run_dir=run_dir)
|
|
104
|
+
return parser.parse_args(argv)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def main(argv: list[str] | None = None) -> int:
|
|
108
|
+
"""Run the CLI entry point."""
|
|
109
|
+
tokens: list[str] = list(argv if argv is not None else sys.argv[1:])
|
|
110
|
+
|
|
111
|
+
# --- Alias resolution (one level only) ---
|
|
112
|
+
run_dir = _resolve_run_dir_for_alias(tokens)
|
|
113
|
+
ext_aliases = _collect_ext_default_aliases(run_dir)
|
|
114
|
+
from arctx_cli.alias import load_alias_table, resolve_alias # noqa: PLC0415
|
|
115
|
+
|
|
116
|
+
alias_table = load_alias_table(
|
|
117
|
+
run_dir=run_dir,
|
|
118
|
+
extensions_default_aliases=ext_aliases,
|
|
119
|
+
)
|
|
120
|
+
tokens = resolve_alias(alias_table, tokens)
|
|
121
|
+
# ---
|
|
122
|
+
|
|
123
|
+
parser = _build_parser(run_dir=run_dir)
|
|
124
|
+
args = parser.parse_args(tokens)
|
|
125
|
+
handler = getattr(args, "_arctx_handler", None)
|
|
126
|
+
if handler is not None:
|
|
127
|
+
return handler(args)
|
|
128
|
+
|
|
129
|
+
return 1
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
if __name__ == "__main__":
|
|
133
|
+
sys.exit(main())
|
arctx_cli/paths.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""ARCTX path resolution — re-exports from arctx.paths.
|
|
2
|
+
|
|
3
|
+
All path logic lives in arctx.paths. This module re-exports the public
|
|
4
|
+
symbols so that CLI code can use ``from arctx_cli.paths import ...``.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from arctx.paths import ( # noqa: F401
|
|
8
|
+
find_repo_root,
|
|
9
|
+
read_arctx_id,
|
|
10
|
+
resolve_git_dir,
|
|
11
|
+
resolve_arctx_home,
|
|
12
|
+
resolve_store_dir,
|
|
13
|
+
runs_dir,
|
|
14
|
+
arctx_id_path,
|
|
15
|
+
write_arctx_id,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
__all__ = [
|
|
19
|
+
"find_repo_root",
|
|
20
|
+
"read_arctx_id",
|
|
21
|
+
"resolve_git_dir",
|
|
22
|
+
"resolve_arctx_home",
|
|
23
|
+
"resolve_store_dir",
|
|
24
|
+
"runs_dir",
|
|
25
|
+
"arctx_id_path",
|
|
26
|
+
"write_arctx_id",
|
|
27
|
+
]
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""Re-export payload builder helpers from arctx.payload_builder.
|
|
2
|
+
|
|
3
|
+
The implementation lives in arctx so that arctx-tui and other non-CLI
|
|
4
|
+
consumers can use it without depending on arctx-cli.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from arctx.payload_builder import ( # noqa: F401
|
|
8
|
+
_dict_field,
|
|
9
|
+
build_payload,
|
|
10
|
+
parse_field_args,
|
|
11
|
+
parse_json_object,
|
|
12
|
+
payload_schema,
|
|
13
|
+
payload_type_names,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
"_dict_field",
|
|
18
|
+
"build_payload",
|
|
19
|
+
"parse_field_args",
|
|
20
|
+
"parse_json_object",
|
|
21
|
+
"payload_schema",
|
|
22
|
+
"payload_type_names",
|
|
23
|
+
]
|
arctx_cli/workspace.py
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""Workspace-local cursor persistence.
|
|
2
|
+
|
|
3
|
+
Cursors are view state for UI and wrapper commands. Core RunHandle
|
|
4
|
+
writers must not read this module.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
from dataclasses import dataclass, field
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
from arctx.core.types import JSONValue, to_jsonable
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass(frozen=True)
|
|
17
|
+
class CursorRecord:
|
|
18
|
+
cursor_id: str
|
|
19
|
+
user_id: str
|
|
20
|
+
run_id: str
|
|
21
|
+
target_kind: str
|
|
22
|
+
target_id: str
|
|
23
|
+
label: str = ""
|
|
24
|
+
metadata: dict[str, JSONValue] = field(default_factory=dict)
|
|
25
|
+
|
|
26
|
+
def to_dict(self) -> dict[str, JSONValue]:
|
|
27
|
+
return to_jsonable(self) # type: ignore[return-value]
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def workspace_dir(store_dir: str) -> Path:
|
|
31
|
+
return Path(store_dir).parent / "workspace"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def cursors_path(store_dir: str) -> Path:
|
|
35
|
+
return workspace_dir(store_dir) / "cursors.json"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def load_cursors(store_dir: str) -> dict[str, CursorRecord]:
|
|
39
|
+
path = cursors_path(store_dir)
|
|
40
|
+
if not path.exists():
|
|
41
|
+
return {}
|
|
42
|
+
data = json.loads(path.read_text(encoding="utf-8"))
|
|
43
|
+
return {
|
|
44
|
+
cursor_id: CursorRecord(**record)
|
|
45
|
+
for cursor_id, record in data.get("cursors", {}).items()
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def save_cursor(record: CursorRecord, store_dir: str) -> Path:
|
|
50
|
+
cursors = load_cursors(store_dir)
|
|
51
|
+
cursors[record.cursor_id] = record
|
|
52
|
+
path = cursors_path(store_dir)
|
|
53
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
54
|
+
path.write_text(
|
|
55
|
+
json.dumps(
|
|
56
|
+
{"cursors": {key: value.to_dict() for key, value in cursors.items()}},
|
|
57
|
+
ensure_ascii=False,
|
|
58
|
+
indent=2,
|
|
59
|
+
sort_keys=True,
|
|
60
|
+
)
|
|
61
|
+
+ "\n",
|
|
62
|
+
encoding="utf-8",
|
|
63
|
+
)
|
|
64
|
+
return path
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: arctx-cli
|
|
3
|
+
Version: 0.2.0b2
|
|
4
|
+
Summary: ARCTX CLI: command-line interface for arctx
|
|
5
|
+
Project-URL: Homepage, https://github.com/takumiecd/stag
|
|
6
|
+
Project-URL: Repository, https://github.com/takumiecd/stag
|
|
7
|
+
Author: Takumi Ishida
|
|
8
|
+
License: MIT
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Requires-Python: >=3.10
|
|
17
|
+
Requires-Dist: arctx>=0.2.0b2
|
|
18
|
+
Provides-Extra: dev
|
|
19
|
+
Requires-Dist: black>=23.0; extra == 'dev'
|
|
20
|
+
Requires-Dist: mypy>=1.0; extra == 'dev'
|
|
21
|
+
Requires-Dist: pytest-asyncio>=0.21; extra == 'dev'
|
|
22
|
+
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
23
|
+
Requires-Dist: ruff>=0.1.0; extra == 'dev'
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
|
|
26
|
+
# arctx-cli
|
|
27
|
+
|
|
28
|
+
Command-line interface and TUI for ARCTX (Arc + Context).
|
|
29
|
+
|
|
30
|
+
## Installation
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
pip install arctx-cli
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
This also installs `arctx` as a dependency.
|
|
37
|
+
|
|
38
|
+
## Usage
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
arctx init
|
|
42
|
+
arctx transition create --from <node_id> --payload-type <type>
|
|
43
|
+
arctx dump
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Package layout
|
|
47
|
+
|
|
48
|
+
This package provides the `arctx` CLI command and TUI. The core API is in the separate `arctx` package.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
arctx_cli/__init__.py,sha256=tDpQZYOaVdRiUQlhxcxtS0ujm9uxGXH16GO_DkmgtP8,25
|
|
2
|
+
arctx_cli/alias.py,sha256=JQsuVnTp7mxIXKPSyQZIjWdpurE_4lJSKUHGNImCiyo,7488
|
|
3
|
+
arctx_cli/append_batch.py,sha256=HoM7x1_MtFlhd_C4HRU2rUrVB_3LhOXApvK6TzSi8K8,3085
|
|
4
|
+
arctx_cli/context.py,sha256=R9Le8Izkb30jQDql3MT2JdmhZHmCc0uxvkx64Ok10yo,1210
|
|
5
|
+
arctx_cli/ext_registry.py,sha256=q6k_f21QWpJuhKdEHIXxQ9yLBy-xSYICwedc4S6O3gk,1114
|
|
6
|
+
arctx_cli/main.py,sha256=dvMBhxm_f3SDIaeX7XBrzwiwCITsGE_jSIWp1le3dO8,3996
|
|
7
|
+
arctx_cli/paths.py,sha256=bBf-nNrFEP5753ELHSTtFxyvIoc8mbI_acCOEzVzCkI,598
|
|
8
|
+
arctx_cli/payload_builder.py,sha256=VS4xw-DUOrdLW1wFtp5vzNPRe5DmfBvSQkEhwVRMY1w,523
|
|
9
|
+
arctx_cli/workspace.py,sha256=BBC55RAVdSVdbS9y10x-FSqEEMevgxq_-AgDeTwlH9U,1670
|
|
10
|
+
arctx_cli/commands/__init__.py,sha256=VirPNWS9gN7XTIoaXAL_mIRMULzfwyeD0sz4TOv4d8M,4600
|
|
11
|
+
arctx_cli/commands/alias_cmd.py,sha256=67CHgfZMQ7Ooj7AEQo3q-MaoWWCPoyPZqQIJMJUXI64,5972
|
|
12
|
+
arctx_cli/commands/anchor.py,sha256=RoLIIyGQS4lRYHocfNv3i_sKxMjUJ87xB7RaFEQdSWk,2359
|
|
13
|
+
arctx_cli/commands/current.py,sha256=BPxOR0fynkLtt4DOMaGflX018oPS9F0_NGngwOaZUIA,1929
|
|
14
|
+
arctx_cli/commands/cut.py,sha256=lSXE_JJsM-dqsdODrdCy-6a-9Njoqsr8pQ8dqB6p0hY,2811
|
|
15
|
+
arctx_cli/commands/dump.py,sha256=Ky8dIDf_9fACI_1Ee5fglWlaBCnT--weJsrz-zc96v4,2554
|
|
16
|
+
arctx_cli/commands/ext.py,sha256=88VwLJtRldDhVsAolUdN_HLKG7fKByG5Oo6q8LwrkIQ,7463
|
|
17
|
+
arctx_cli/commands/git.py,sha256=LsNuroSTcq0pACIQu8A3n6I-FZ9f65gD0S0R4BavcMA,7590
|
|
18
|
+
arctx_cli/commands/graph.py,sha256=vPu2mpehPCiNu9ltlqAszi-iA8G9ROPWLzQkv4IBU-c,2740
|
|
19
|
+
arctx_cli/commands/guide.py,sha256=CNURTljNgV9kbDt8Ht7FmPh-Qbxa0gW29UY2uIsAbU4,11815
|
|
20
|
+
arctx_cli/commands/init.py,sha256=58J4LbV7kKFXIk7WJ5FCZ95Y7fVrWhg3e7xxNnFrei0,7531
|
|
21
|
+
arctx_cli/commands/list.py,sha256=hZ22SnPOlKO6g2FM9GPSa_U4yjicgPfpa9BanVqbhGU,1111
|
|
22
|
+
arctx_cli/commands/migrate.py,sha256=egWT_lAaFWbtX8XhM_VhtlFoazOnkiaOZOUmUiLnuEQ,3973
|
|
23
|
+
arctx_cli/commands/node.py,sha256=amoC7gnUvLMMIA1sHJ70K-MkaHQzz61NH06MpJiBupQ,1793
|
|
24
|
+
arctx_cli/commands/outcomes.py,sha256=rhJ6zZMZlskYtTaFSourlRvQCGEl3lzco8OjdoptRgE,1695
|
|
25
|
+
arctx_cli/commands/payload.py,sha256=0yrEfP9JoeJVVLUTydaKeGUcIBD2o-1e8ztTB2EZviw,7074
|
|
26
|
+
arctx_cli/commands/reachable.py,sha256=oIYtOXcxDu3N4UGc-_E3N-pK3WSzW8QIrnATyhdxdds,2422
|
|
27
|
+
arctx_cli/commands/show.py,sha256=W3DECjql6STWbrjcntT2RhhnnFIqa-zPKBGj_lquVnE,3987
|
|
28
|
+
arctx_cli/commands/sync.py,sha256=OkYethlMklqYR4S7tOhoa5jzrdQ_6dNEKGF1xxGldaQ,7536
|
|
29
|
+
arctx_cli/commands/trace.py,sha256=gecrPriUo15OUvSZF8PwK9sV9HpVnzI1ZkpzaAn2VbY,1252
|
|
30
|
+
arctx_cli/commands/transition.py,sha256=7-6kChKCVCfSHIaDNHUASpVr-_jz7BwiyjzqHlpDVJA,7410
|
|
31
|
+
arctx_cli/commands/use.py,sha256=OpRvIxKEMTFisqEbGglv0zrMzBypT01W6ZNUoXyK8S0,1801
|
|
32
|
+
arctx_cli/commands/view.py,sha256=U7o7Co4VTfXfcEmNgUFf6zjcN-GOVkk7WVSnBEchYzo,2761
|
|
33
|
+
arctx_cli/commands/work_session.py,sha256=yvFJTZisZMDanm7s-FiOpOfpUES4kVwWbubgrW0lPVE,11813
|
|
34
|
+
arctx_cli/ext/__init__.py,sha256=Ju-TMDVdaYLxi5y60gzj7YWk9mp-3Jq10atMqhxl4a0,38
|
|
35
|
+
arctx_cli/ext/command/__init__.py,sha256=NZhLiTrwNUpwnOgtXAJ4QmpCKek2UUKGHlwq4MmdT9g,3543
|
|
36
|
+
arctx_cli/ext/git/__init__.py,sha256=vLdXnqplyPPu-3F4oFPN46RwaDdxvrEC5UyjSOqk7pw,24
|
|
37
|
+
arctx_cli/ext/git/branch.py,sha256=0_wnyRh9x8_C2stRiuFdIGHihbhUlcxvdVJV8e_S5Gw,4724
|
|
38
|
+
arctx_cli/ext/git/cherry_pick.py,sha256=vzb_zcgm7t8n9JGi7xTgHE3tj4h-hc6vEpBFf5Ay0h0,4422
|
|
39
|
+
arctx_cli/ext/git/commit.py,sha256=Mo3G7ecId9rY90cC_y2YNFGw4V7pbrwXhMMRL8PKNSY,6368
|
|
40
|
+
arctx_cli/ext/git/hook.py,sha256=uwNMagpJJb3fINgY9FjaLCBS06njcG1SiRFcEO68SeM,26489
|
|
41
|
+
arctx_cli/ext/git/merge.py,sha256=wXQZWhlhYgZWW8sVvpj44xHF1D9aKbXE3tC9Lhs3abU,6064
|
|
42
|
+
arctx_cli/ext/git/reset.py,sha256=vHjD5YYvxPmkDyQ7ekT6PYjqI1Bg2o75phoUZJosfkQ,3845
|
|
43
|
+
arctx_cli/ext/git/revert.py,sha256=JOK6JzBG1Su1awU1kqw9dDWDIxPYLGP-0AG_srTxKtE,5010
|
|
44
|
+
arctx_cli/ext/git/verify.py,sha256=jhfDYqe3EhMgpZv17PLhwUyuT-FEjVgwKzwZB1_av4k,3861
|
|
45
|
+
arctx_cli/ext/git/worktree.py,sha256=T-jeswPQkNyuui3ROgOguaNkaqx7P-twmT6cbLOncgA,5698
|
|
46
|
+
arctx_cli-0.2.0b2.dist-info/METADATA,sha256=biiMYiIGb8EwWOZs-6Q9iTXe6QlBunMqsRRgQtCkp1o,1335
|
|
47
|
+
arctx_cli-0.2.0b2.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
48
|
+
arctx_cli-0.2.0b2.dist-info/entry_points.txt,sha256=GF0iaqs7ahYtV7IBIvm1Y_DuII2cDJicSD5UZNLr-RU,46
|
|
49
|
+
arctx_cli-0.2.0b2.dist-info/RECORD,,
|