arctx-cli 0.2.0b3__tar.gz → 0.3.0b1__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.
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/.gitignore +2 -1
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/PKG-INFO +3 -3
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/pyproject.toml +3 -3
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/src/arctx_cli/append_batch.py +4 -12
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/src/arctx_cli/commands/__init__.py +9 -30
- arctx_cli-0.3.0b1/src/arctx_cli/commands/_targets.py +38 -0
- arctx_cli-0.3.0b1/src/arctx_cli/commands/add.py +203 -0
- arctx_cli-0.3.0b1/src/arctx_cli/commands/attach.py +91 -0
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/src/arctx_cli/commands/cut.py +27 -14
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/src/arctx_cli/commands/dump.py +2 -2
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/src/arctx_cli/commands/export.py +1 -1
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/src/arctx_cli/commands/init.py +3 -2
- arctx_cli-0.3.0b1/src/arctx_cli/commands/log.py +66 -0
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/src/arctx_cli/commands/migrate.py +7 -2
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/src/arctx_cli/commands/node.py +2 -2
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/src/arctx_cli/commands/outcomes.py +7 -7
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/src/arctx_cli/commands/payload.py +12 -12
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/src/arctx_cli/commands/reachable.py +10 -16
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/src/arctx_cli/commands/show.py +72 -19
- arctx_cli-0.2.0b3/src/arctx_cli/commands/transition.py → arctx_cli-0.3.0b1/src/arctx_cli/commands/step.py +37 -37
- arctx_cli-0.3.0b1/src/arctx_cli/ext/claude_code/__init__.py +320 -0
- arctx_cli-0.3.0b1/src/arctx_cli/ext/codex/__init__.py +302 -0
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/src/arctx_cli/ext/command/__init__.py +1 -1
- arctx_cli-0.2.0b3/src/arctx_cli/commands/git.py → arctx_cli-0.3.0b1/src/arctx_cli/ext/git/__init__.py +18 -18
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/src/arctx_cli/ext/git/branch.py +11 -11
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/src/arctx_cli/ext/git/cherry_pick.py +16 -16
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/src/arctx_cli/ext/git/commit.py +35 -14
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/src/arctx_cli/ext/git/hook.py +43 -43
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/src/arctx_cli/ext/git/merge.py +12 -12
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/src/arctx_cli/ext/git/reset.py +7 -7
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/src/arctx_cli/ext/git/revert.py +23 -23
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/src/arctx_cli/ext/git/verify.py +10 -10
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/src/arctx_cli/ext_registry.py +13 -3
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/src/arctx_cli/main.py +6 -1
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/tests/cli/test_basic.py +16 -53
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/tests/cli/test_branch.py +4 -4
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/tests/cli/test_cherry_pick.py +8 -8
- arctx_cli-0.3.0b1/tests/cli/test_claude_code_cmd.py +267 -0
- arctx_cli-0.3.0b1/tests/cli/test_codex_cmd.py +220 -0
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/tests/cli/test_commit.py +61 -3
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/tests/cli/test_commit_guard.py +4 -4
- arctx_cli-0.3.0b1/tests/cli/test_dag_core_cli.py +189 -0
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/tests/cli/test_git_namespace_cli.py +1 -1
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/tests/cli/test_hook_post_merge.py +3 -3
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/tests/cli/test_hook_post_rewrite.py +10 -10
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/tests/cli/test_merge.py +17 -17
- arctx_cli-0.3.0b1/tests/cli/test_migrate.py +48 -0
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/tests/cli/test_reset.py +12 -12
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/tests/cli/test_revert.py +13 -13
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/tests/cli/test_show_history.py +18 -18
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/tests/cli/test_verify.py +3 -3
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/tests/cli/test_work_session.py +3 -3
- arctx_cli-0.2.0b3/src/arctx_cli/commands/anchor.py +0 -82
- arctx_cli-0.2.0b3/src/arctx_cli/commands/guide.py +0 -360
- arctx_cli-0.2.0b3/src/arctx_cli/commands/sync.py +0 -244
- arctx_cli-0.2.0b3/src/arctx_cli/commands/view.py +0 -82
- arctx_cli-0.2.0b3/src/arctx_cli/ext/git/__init__.py +0 -1
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/README.md +0 -0
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/src/arctx_cli/__init__.py +0 -0
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/src/arctx_cli/alias.py +0 -0
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/src/arctx_cli/commands/alias_cmd.py +0 -0
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/src/arctx_cli/commands/current.py +0 -0
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/src/arctx_cli/commands/ext.py +0 -0
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/src/arctx_cli/commands/graph.py +0 -0
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/src/arctx_cli/commands/list.py +0 -0
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/src/arctx_cli/commands/trace.py +0 -0
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/src/arctx_cli/commands/use.py +0 -0
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/src/arctx_cli/commands/work_session.py +0 -0
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/src/arctx_cli/context.py +0 -0
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/src/arctx_cli/ext/__init__.py +0 -0
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/src/arctx_cli/ext/git/repo.py +0 -0
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/src/arctx_cli/ext/git/worktree.py +0 -0
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/src/arctx_cli/paths.py +0 -0
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/src/arctx_cli/payload_builder.py +0 -0
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/src/arctx_cli/workspace.py +0 -0
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/tests/__init__.py +0 -0
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/tests/cli/__init__.py +0 -0
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/tests/cli/test_alias_cli.py +0 -0
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/tests/cli/test_alias_resolve.py +0 -0
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/tests/cli/test_command_extension.py +0 -0
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/tests/cli/test_ext_cli.py +0 -0
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/tests/cli/test_git_repo.py +0 -0
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/tests/cli/test_git_worktree.py +0 -0
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/tests/cli/test_hook_install.py +0 -0
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/tests/cli/test_init_extension.py +0 -0
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/tests/cli/test_init_hooks.py +0 -0
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/tests/cli/test_init_stag_id.py +0 -0
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/tests/cli/test_paths.py +0 -0
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/tests/fixtures/__init__.py +0 -0
- {arctx_cli-0.2.0b3 → arctx_cli-0.3.0b1}/tests/fixtures/dummy_ext.py +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: arctx-cli
|
|
3
|
-
Version: 0.
|
|
4
|
-
Summary: CLI for ARCTX — append-only DAG
|
|
3
|
+
Version: 0.3.0b1
|
|
4
|
+
Summary: CLI for ARCTX — append-only DAG editing and refactoring
|
|
5
5
|
Project-URL: Homepage, https://github.com/takumiecd/arctx
|
|
6
6
|
Project-URL: Repository, https://github.com/takumiecd/arctx
|
|
7
7
|
Author: Takumi Ishida
|
|
@@ -17,7 +17,7 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
17
17
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
18
18
|
Classifier: Topic :: Utilities
|
|
19
19
|
Requires-Python: >=3.10
|
|
20
|
-
Requires-Dist: arctx>=0.
|
|
20
|
+
Requires-Dist: arctx>=0.3.0b1
|
|
21
21
|
Provides-Extra: dev
|
|
22
22
|
Requires-Dist: black>=23.0; extra == 'dev'
|
|
23
23
|
Requires-Dist: mypy>=1.0; extra == 'dev'
|
|
@@ -4,8 +4,8 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "arctx-cli"
|
|
7
|
-
version = "0.
|
|
8
|
-
description = "CLI for ARCTX — append-only DAG
|
|
7
|
+
version = "0.3.0b1"
|
|
8
|
+
description = "CLI for ARCTX — append-only DAG editing and refactoring"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
11
11
|
license = {text = "MIT"}
|
|
@@ -25,7 +25,7 @@ classifiers = [
|
|
|
25
25
|
"Topic :: Utilities",
|
|
26
26
|
]
|
|
27
27
|
dependencies = [
|
|
28
|
-
"arctx>=0.
|
|
28
|
+
"arctx>=0.3.0b1",
|
|
29
29
|
]
|
|
30
30
|
|
|
31
31
|
[project.optional-dependencies]
|
|
@@ -9,9 +9,8 @@ def graph_counts(handle) -> dict[str, set[str]]:
|
|
|
9
9
|
"""Capture graph record IDs before a mutation."""
|
|
10
10
|
return {
|
|
11
11
|
"nodes": set(handle.run_graph.nodes),
|
|
12
|
-
"
|
|
12
|
+
"steps": set(handle.run_graph.steps),
|
|
13
13
|
"payloads": set(handle.run_graph.payloads),
|
|
14
|
-
"views": {view.view_id for view in handle.run_graph.views.values()},
|
|
15
14
|
"work_events": {event.event_id for event in handle.run_graph.work_events},
|
|
16
15
|
}
|
|
17
16
|
|
|
@@ -52,21 +51,14 @@ def build_append_batch(
|
|
|
52
51
|
node = handle.run_graph.nodes[node_id]
|
|
53
52
|
records.append(GraphRecordEnvelope("node", node.node_id, node))
|
|
54
53
|
|
|
55
|
-
for
|
|
56
|
-
|
|
57
|
-
records.append(GraphRecordEnvelope("
|
|
54
|
+
for step_id in _new_ids(handle.run_graph.steps, before, "steps"):
|
|
55
|
+
step = handle.run_graph.steps[step_id]
|
|
56
|
+
records.append(GraphRecordEnvelope("step", step.step_id, step))
|
|
58
57
|
|
|
59
58
|
for payload_id in _new_ids(handle.run_graph.payloads, before, "payloads"):
|
|
60
59
|
payload = handle.run_graph.payloads[payload_id]
|
|
61
60
|
records.append(GraphRecordEnvelope("payload", payload.payload_id, payload))
|
|
62
61
|
|
|
63
|
-
before_view_ids = before.get("views", set())
|
|
64
|
-
new_views = [
|
|
65
|
-
view for view in handle.run_graph.views.values() if view.view_id not in before_view_ids
|
|
66
|
-
]
|
|
67
|
-
for view in new_views:
|
|
68
|
-
records.append(GraphRecordEnvelope("view", view.view_id, view))
|
|
69
|
-
|
|
70
62
|
new_events = [
|
|
71
63
|
event
|
|
72
64
|
for event in handle.run_graph.work_events
|
|
@@ -7,10 +7,12 @@ from arctx.ext import CliCommand
|
|
|
7
7
|
|
|
8
8
|
def core_cli_commands() -> list[CliCommand]:
|
|
9
9
|
"""Return the built-in, extension-independent CLI commands."""
|
|
10
|
+
from arctx_cli.commands.add import add_parser as add_add_parser
|
|
11
|
+
from arctx_cli.commands.add import cli_add
|
|
10
12
|
from arctx_cli.commands.alias_cmd import add_parser as add_alias_parser
|
|
11
13
|
from arctx_cli.commands.alias_cmd import cli_alias
|
|
12
|
-
from arctx_cli.commands.
|
|
13
|
-
from arctx_cli.commands.
|
|
14
|
+
from arctx_cli.commands.attach import add_parser as add_attach_parser
|
|
15
|
+
from arctx_cli.commands.attach import cli_attach
|
|
14
16
|
from arctx_cli.commands.current import add_parser as add_current_parser
|
|
15
17
|
from arctx_cli.commands.current import cli_current
|
|
16
18
|
from arctx_cli.commands.cut import add_parser as add_cut_parser
|
|
@@ -23,60 +25,37 @@ def core_cli_commands() -> list[CliCommand]:
|
|
|
23
25
|
from arctx_cli.commands.ext import cli_ext
|
|
24
26
|
from arctx_cli.commands.graph import add_parser as add_graph_parser
|
|
25
27
|
from arctx_cli.commands.graph import cli_graph
|
|
26
|
-
from arctx_cli.commands.guide import add_parser as add_guide_parser
|
|
27
|
-
from arctx_cli.commands.guide import cli_guide
|
|
28
28
|
from arctx_cli.commands.init import add_parser as add_init_parser
|
|
29
29
|
from arctx_cli.commands.init import cli_init
|
|
30
30
|
from arctx_cli.commands.list import add_parser as add_list_parser
|
|
31
31
|
from arctx_cli.commands.list import cli_list
|
|
32
|
+
from arctx_cli.commands.log import add_parser as add_log_parser
|
|
33
|
+
from arctx_cli.commands.log import cli_log
|
|
32
34
|
from arctx_cli.commands.migrate import add_parser as add_migrate_parser
|
|
33
35
|
from arctx_cli.commands.migrate import cli_migrate
|
|
34
|
-
from arctx_cli.commands.node import add_parser as add_node_parser
|
|
35
|
-
from arctx_cli.commands.node import cli_node
|
|
36
|
-
from arctx_cli.commands.outcomes import add_parser as add_outcomes_parser
|
|
37
|
-
from arctx_cli.commands.outcomes import cli_outcomes
|
|
38
|
-
from arctx_cli.commands.payload import add_parser as add_payload_parser
|
|
39
|
-
from arctx_cli.commands.payload import cli_payload
|
|
40
|
-
from arctx_cli.commands.reachable import add_parser as add_reachable_parser
|
|
41
|
-
from arctx_cli.commands.reachable import cli_reachable
|
|
42
36
|
from arctx_cli.commands.show import add_parser as add_show_parser
|
|
43
37
|
from arctx_cli.commands.show import cli_show
|
|
44
|
-
from arctx_cli.commands.sync import add_parser as add_sync_parser
|
|
45
|
-
from arctx_cli.commands.sync import cli_sync
|
|
46
|
-
from arctx_cli.commands.trace import add_parser as add_trace_parser
|
|
47
|
-
from arctx_cli.commands.trace import cli_trace
|
|
48
|
-
from arctx_cli.commands.transition import add_parser as add_transition_parser
|
|
49
|
-
from arctx_cli.commands.transition import cli_transition
|
|
50
38
|
from arctx_cli.commands.use import add_parser as add_use_parser
|
|
51
39
|
from arctx_cli.commands.use import cli_use
|
|
52
|
-
from arctx_cli.commands.view import add_parser as add_view_parser
|
|
53
|
-
from arctx_cli.commands.view import cli_view
|
|
54
40
|
from arctx_cli.commands.work_session import add_parser as add_work_session_parser
|
|
55
41
|
from arctx_cli.commands.work_session import cli_work_session
|
|
56
42
|
|
|
57
43
|
return [
|
|
44
|
+
CliCommand("add", add_add_parser, cli_add),
|
|
58
45
|
CliCommand("alias", add_alias_parser, cli_alias),
|
|
59
|
-
CliCommand("
|
|
46
|
+
CliCommand("attach", add_attach_parser, cli_attach),
|
|
60
47
|
CliCommand("current", add_current_parser, cli_current),
|
|
61
48
|
CliCommand("ext", add_ext_parser, cli_ext),
|
|
62
49
|
CliCommand("dump", add_dump_parser, cli_dump),
|
|
63
50
|
CliCommand("export", add_export_parser, cli_export),
|
|
64
51
|
CliCommand("graph", add_graph_parser, cli_graph),
|
|
65
|
-
CliCommand("guide", add_guide_parser, cli_guide),
|
|
66
52
|
CliCommand("init", add_init_parser, cli_init),
|
|
67
53
|
CliCommand("list", add_list_parser, cli_list),
|
|
54
|
+
CliCommand("log", add_log_parser, cli_log),
|
|
68
55
|
CliCommand("migrate", add_migrate_parser, cli_migrate),
|
|
69
|
-
CliCommand("node", add_node_parser, cli_node),
|
|
70
|
-
CliCommand("outcomes", add_outcomes_parser, cli_outcomes),
|
|
71
|
-
CliCommand("payload", add_payload_parser, cli_payload),
|
|
72
|
-
CliCommand("reachable", add_reachable_parser, cli_reachable),
|
|
73
56
|
CliCommand("cut", add_cut_parser, cli_cut),
|
|
74
57
|
CliCommand("show", add_show_parser, cli_show),
|
|
75
|
-
CliCommand("sync", add_sync_parser, cli_sync),
|
|
76
|
-
CliCommand("trace", add_trace_parser, cli_trace),
|
|
77
|
-
CliCommand("transition", add_transition_parser, cli_transition),
|
|
78
58
|
CliCommand("use", add_use_parser, cli_use),
|
|
79
|
-
CliCommand("view", add_view_parser, cli_view),
|
|
80
59
|
CliCommand("work-session", add_work_session_parser, cli_work_session),
|
|
81
60
|
]
|
|
82
61
|
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""Helpers for resolving user-facing DAG record IDs."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Literal
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
TargetKind = Literal["node", "step", "payload"]
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def resolve_target_kind(handle, record_id: str) -> TargetKind:
|
|
12
|
+
"""Resolve a record id to its internal target kind."""
|
|
13
|
+
graph = handle.run_graph
|
|
14
|
+
matches: list[TargetKind] = []
|
|
15
|
+
if record_id in graph.nodes:
|
|
16
|
+
matches.append("node")
|
|
17
|
+
if record_id in graph.steps:
|
|
18
|
+
matches.append("step")
|
|
19
|
+
if record_id in graph.payloads:
|
|
20
|
+
matches.append("payload")
|
|
21
|
+
if not matches:
|
|
22
|
+
raise KeyError(f"unknown record_id: {record_id}")
|
|
23
|
+
if len(matches) > 1:
|
|
24
|
+
raise ValueError(f"ambiguous record_id {record_id!r}: {matches}")
|
|
25
|
+
return matches[0]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def step_view(step) -> dict:
|
|
29
|
+
"""Return a user-facing Step view for an internal Step."""
|
|
30
|
+
return {
|
|
31
|
+
"kind": "step",
|
|
32
|
+
"id": step.step_id,
|
|
33
|
+
"step_id": step.step_id,
|
|
34
|
+
"step_id": step.step_id,
|
|
35
|
+
"input_node_ids": list(step.input_node_ids),
|
|
36
|
+
"output_node_id": step.output_node_id,
|
|
37
|
+
"metadata": dict(step.metadata),
|
|
38
|
+
}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
"""User-facing arctx add commands."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import json
|
|
7
|
+
import sys
|
|
8
|
+
|
|
9
|
+
from arctx_cli.append_batch import graph_counts, maybe_append_or_save
|
|
10
|
+
from arctx_cli.commands._targets import step_view
|
|
11
|
+
from arctx_cli.context import (
|
|
12
|
+
resolve_run_id_from_args,
|
|
13
|
+
resolve_store,
|
|
14
|
+
resolve_user_id_from_args,
|
|
15
|
+
resolve_work_session_id_from_args,
|
|
16
|
+
)
|
|
17
|
+
from arctx_cli.payload_builder import build_payload, parse_field_args, parse_json_object
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def add_parser(subparsers) -> argparse.ArgumentParser:
|
|
21
|
+
parser = subparsers.add_parser("add", help="Add DAG nodes or steps")
|
|
22
|
+
add_sub = parser.add_subparsers(dest="add_command", required=True)
|
|
23
|
+
|
|
24
|
+
sp_node = add_sub.add_parser("node", help="Add a standalone Node")
|
|
25
|
+
sp_node.add_argument("--title", default=None)
|
|
26
|
+
sp_node.add_argument("--type", dest="payload_kind", default=None)
|
|
27
|
+
sp_node.add_argument("--payload-type", default="node_payload")
|
|
28
|
+
sp_node.add_argument("--field", action="append", default=None, help="Payload field as key=value")
|
|
29
|
+
sp_node.add_argument("--json", default=None, help="Payload fields as a JSON object")
|
|
30
|
+
sp_node.add_argument("--run", default=None)
|
|
31
|
+
sp_node.add_argument("--store-dir", default=None)
|
|
32
|
+
sp_node.add_argument("--user", default=None)
|
|
33
|
+
sp_node.add_argument("--work-session", default=None)
|
|
34
|
+
|
|
35
|
+
sp_step = add_sub.add_parser("step", help="Add a Step from input Nodes")
|
|
36
|
+
sp_step.add_argument(
|
|
37
|
+
"--from",
|
|
38
|
+
action="append",
|
|
39
|
+
required=True,
|
|
40
|
+
dest="input_nodes",
|
|
41
|
+
metavar="NODE_ID",
|
|
42
|
+
help="Input node (repeatable for multi-input steps)",
|
|
43
|
+
)
|
|
44
|
+
sp_step.add_argument("--title", default=None)
|
|
45
|
+
sp_step.add_argument("--type", dest="payload_kind", default=None)
|
|
46
|
+
sp_step.add_argument("--payload-type", default="step_payload")
|
|
47
|
+
sp_step.add_argument("--field", action="append", default=None, help="Payload field as key=value")
|
|
48
|
+
sp_step.add_argument("--json", default=None, help="Payload fields as a JSON object")
|
|
49
|
+
sp_step.add_argument("--run", default=None)
|
|
50
|
+
sp_step.add_argument("--store-dir", default=None)
|
|
51
|
+
sp_step.add_argument("--user", default=None)
|
|
52
|
+
sp_step.add_argument("--work-session", default=None)
|
|
53
|
+
|
|
54
|
+
return parser
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def run_add_node_command(
|
|
58
|
+
*,
|
|
59
|
+
run_id: str,
|
|
60
|
+
title: str | None,
|
|
61
|
+
payload_kind: str | None,
|
|
62
|
+
payload_type: str,
|
|
63
|
+
field_data: dict,
|
|
64
|
+
json_data: dict,
|
|
65
|
+
store_dir: str,
|
|
66
|
+
user_id: str | None = None,
|
|
67
|
+
work_session_id: str | None = None,
|
|
68
|
+
) -> dict:
|
|
69
|
+
store = resolve_store(store_dir)
|
|
70
|
+
if not store.run_path(run_id).exists():
|
|
71
|
+
raise KeyError(f"unknown run_id: {run_id}")
|
|
72
|
+
handle = store.load_run(run_id)
|
|
73
|
+
before = graph_counts(handle)
|
|
74
|
+
node = handle.add_node(user_id=user_id, work_session_id=work_session_id)
|
|
75
|
+
|
|
76
|
+
data = dict(json_data or {})
|
|
77
|
+
data.update(field_data or {})
|
|
78
|
+
if title is not None:
|
|
79
|
+
data.setdefault("title", title)
|
|
80
|
+
data.setdefault("text", title)
|
|
81
|
+
if payload_kind is not None:
|
|
82
|
+
data.setdefault("type", payload_kind)
|
|
83
|
+
elif data:
|
|
84
|
+
data.setdefault("type", "node")
|
|
85
|
+
|
|
86
|
+
payload = None
|
|
87
|
+
if data:
|
|
88
|
+
payload = build_payload(
|
|
89
|
+
payload_type=payload_type,
|
|
90
|
+
target_kind="node",
|
|
91
|
+
target_id=node.node_id,
|
|
92
|
+
payload_id=handle._next_id("pl"),
|
|
93
|
+
json_data={},
|
|
94
|
+
field_data=data,
|
|
95
|
+
)
|
|
96
|
+
attached = handle.attach(
|
|
97
|
+
node.node_id,
|
|
98
|
+
payload,
|
|
99
|
+
user_id=user_id,
|
|
100
|
+
work_session_id=work_session_id,
|
|
101
|
+
)
|
|
102
|
+
payload = attached
|
|
103
|
+
|
|
104
|
+
maybe_append_or_save(
|
|
105
|
+
store=store,
|
|
106
|
+
handle=handle,
|
|
107
|
+
user_id=user_id,
|
|
108
|
+
work_session_id=work_session_id,
|
|
109
|
+
before=before,
|
|
110
|
+
)
|
|
111
|
+
result = {"node": node.to_dict()}
|
|
112
|
+
if payload is not None:
|
|
113
|
+
result["payload"] = payload.to_dict()
|
|
114
|
+
return result
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def run_add_step_command(
|
|
118
|
+
*,
|
|
119
|
+
run_id: str,
|
|
120
|
+
input_node_ids: list[str],
|
|
121
|
+
title: str | None,
|
|
122
|
+
payload_kind: str | None,
|
|
123
|
+
payload_type: str,
|
|
124
|
+
field_data: dict,
|
|
125
|
+
json_data: dict,
|
|
126
|
+
store_dir: str,
|
|
127
|
+
user_id: str | None = None,
|
|
128
|
+
work_session_id: str | None = None,
|
|
129
|
+
) -> dict:
|
|
130
|
+
store = resolve_store(store_dir)
|
|
131
|
+
if not store.run_path(run_id).exists():
|
|
132
|
+
raise KeyError(f"unknown run_id: {run_id}")
|
|
133
|
+
handle = store.load_run(run_id)
|
|
134
|
+
data = dict(json_data or {})
|
|
135
|
+
data.update(field_data or {})
|
|
136
|
+
if title is not None:
|
|
137
|
+
data.setdefault("title", title)
|
|
138
|
+
data.setdefault("text", title)
|
|
139
|
+
if payload_kind is not None:
|
|
140
|
+
data.setdefault("type", payload_kind)
|
|
141
|
+
else:
|
|
142
|
+
data.setdefault("type", "step")
|
|
143
|
+
|
|
144
|
+
payload = build_payload(
|
|
145
|
+
payload_type=payload_type,
|
|
146
|
+
target_kind="step",
|
|
147
|
+
target_id="pending",
|
|
148
|
+
payload_id="pending",
|
|
149
|
+
json_data={},
|
|
150
|
+
field_data=data,
|
|
151
|
+
)
|
|
152
|
+
before = graph_counts(handle)
|
|
153
|
+
step = handle.add_step(
|
|
154
|
+
input_node_ids,
|
|
155
|
+
payload,
|
|
156
|
+
user_id=user_id,
|
|
157
|
+
work_session_id=work_session_id,
|
|
158
|
+
)
|
|
159
|
+
maybe_append_or_save(
|
|
160
|
+
store=store,
|
|
161
|
+
handle=handle,
|
|
162
|
+
user_id=user_id,
|
|
163
|
+
work_session_id=work_session_id,
|
|
164
|
+
before=before,
|
|
165
|
+
)
|
|
166
|
+
return {"step": step_view(step)}
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def cli_add(args) -> int:
|
|
170
|
+
try:
|
|
171
|
+
if args.add_command == "node":
|
|
172
|
+
result = run_add_node_command(
|
|
173
|
+
run_id=resolve_run_id_from_args(args),
|
|
174
|
+
title=args.title,
|
|
175
|
+
payload_kind=args.payload_kind,
|
|
176
|
+
payload_type=args.payload_type,
|
|
177
|
+
field_data=parse_field_args(args.field),
|
|
178
|
+
json_data=parse_json_object(args.json),
|
|
179
|
+
store_dir=args.store_dir,
|
|
180
|
+
user_id=resolve_user_id_from_args(args),
|
|
181
|
+
work_session_id=resolve_work_session_id_from_args(args),
|
|
182
|
+
)
|
|
183
|
+
print(json.dumps(result, ensure_ascii=False, indent=2))
|
|
184
|
+
return 0
|
|
185
|
+
if args.add_command == "step":
|
|
186
|
+
result = run_add_step_command(
|
|
187
|
+
run_id=resolve_run_id_from_args(args),
|
|
188
|
+
input_node_ids=args.input_nodes,
|
|
189
|
+
title=args.title,
|
|
190
|
+
payload_kind=args.payload_kind,
|
|
191
|
+
payload_type=args.payload_type,
|
|
192
|
+
field_data=parse_field_args(args.field),
|
|
193
|
+
json_data=parse_json_object(args.json),
|
|
194
|
+
store_dir=args.store_dir,
|
|
195
|
+
user_id=resolve_user_id_from_args(args),
|
|
196
|
+
work_session_id=resolve_work_session_id_from_args(args),
|
|
197
|
+
)
|
|
198
|
+
print(json.dumps(result["step"], ensure_ascii=False, indent=2))
|
|
199
|
+
return 0
|
|
200
|
+
except (KeyError, ValueError, json.JSONDecodeError) as exc:
|
|
201
|
+
print(f"error: {exc}", file=sys.stderr)
|
|
202
|
+
return 1
|
|
203
|
+
return 1
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"""User-facing arctx attach command."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import json
|
|
7
|
+
import sys
|
|
8
|
+
|
|
9
|
+
from arctx_cli.commands._targets import resolve_target_kind
|
|
10
|
+
from arctx_cli.commands.payload import run_payload_add_command
|
|
11
|
+
from arctx_cli.context import (
|
|
12
|
+
resolve_run_id_from_args,
|
|
13
|
+
resolve_store,
|
|
14
|
+
resolve_user_id_from_args,
|
|
15
|
+
resolve_work_session_id_from_args,
|
|
16
|
+
)
|
|
17
|
+
from arctx_cli.payload_builder import parse_field_args, parse_json_object
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def add_parser(subparsers) -> argparse.ArgumentParser:
|
|
21
|
+
parser = subparsers.add_parser("attach", help="Attach a Payload to a Node or Step")
|
|
22
|
+
parser.add_argument("target_id")
|
|
23
|
+
parser.add_argument("--type", dest="payload_kind", default="payload")
|
|
24
|
+
parser.add_argument("--payload-type", default=None)
|
|
25
|
+
parser.add_argument("--field", action="append", default=None, help="Payload field as key=value")
|
|
26
|
+
parser.add_argument("--json", default=None, help="Payload fields as a JSON object")
|
|
27
|
+
parser.add_argument("--run", default=None)
|
|
28
|
+
parser.add_argument("--store-dir", default=None)
|
|
29
|
+
parser.add_argument("--user", default=None)
|
|
30
|
+
parser.add_argument("--work-session", default=None)
|
|
31
|
+
return parser
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def run_attach_command(
|
|
35
|
+
*,
|
|
36
|
+
run_id: str,
|
|
37
|
+
target_id: str,
|
|
38
|
+
payload_kind: str,
|
|
39
|
+
payload_type: str | None,
|
|
40
|
+
field_data: dict,
|
|
41
|
+
json_data: dict,
|
|
42
|
+
store_dir: str,
|
|
43
|
+
user_id: str | None = None,
|
|
44
|
+
work_session_id: str | None = None,
|
|
45
|
+
) -> dict:
|
|
46
|
+
store = resolve_store(store_dir)
|
|
47
|
+
if not store.run_path(run_id).exists():
|
|
48
|
+
raise KeyError(f"unknown run_id: {run_id}")
|
|
49
|
+
handle = store.load_run(run_id)
|
|
50
|
+
target_kind = resolve_target_kind(handle, target_id)
|
|
51
|
+
if target_kind == "payload":
|
|
52
|
+
raise ValueError("cannot attach a payload to another payload")
|
|
53
|
+
|
|
54
|
+
data = dict(json_data or {})
|
|
55
|
+
data.update(field_data or {})
|
|
56
|
+
data.setdefault("type", payload_kind)
|
|
57
|
+
internal_payload_type = payload_type or (
|
|
58
|
+
"node_payload" if target_kind == "node" else "step_payload"
|
|
59
|
+
)
|
|
60
|
+
result = run_payload_add_command(
|
|
61
|
+
run_id=run_id,
|
|
62
|
+
target_kind=target_kind,
|
|
63
|
+
target_id=target_id,
|
|
64
|
+
payload_type=internal_payload_type,
|
|
65
|
+
field_data=data,
|
|
66
|
+
json_data={},
|
|
67
|
+
store_dir=store_dir,
|
|
68
|
+
user_id=user_id,
|
|
69
|
+
work_session_id=work_session_id,
|
|
70
|
+
)
|
|
71
|
+
return result
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def cli_attach(args) -> int:
|
|
75
|
+
try:
|
|
76
|
+
result = run_attach_command(
|
|
77
|
+
run_id=resolve_run_id_from_args(args),
|
|
78
|
+
target_id=args.target_id,
|
|
79
|
+
payload_kind=args.payload_kind,
|
|
80
|
+
payload_type=args.payload_type,
|
|
81
|
+
field_data=parse_field_args(args.field),
|
|
82
|
+
json_data=parse_json_object(args.json),
|
|
83
|
+
store_dir=args.store_dir,
|
|
84
|
+
user_id=resolve_user_id_from_args(args),
|
|
85
|
+
work_session_id=resolve_work_session_id_from_args(args),
|
|
86
|
+
)
|
|
87
|
+
print(json.dumps(result["payload"], ensure_ascii=False, indent=2))
|
|
88
|
+
return 0
|
|
89
|
+
except (KeyError, ValueError, json.JSONDecodeError) as exc:
|
|
90
|
+
print(f"error: {exc}", file=sys.stderr)
|
|
91
|
+
return 1
|
|
@@ -5,6 +5,7 @@ from __future__ import annotations
|
|
|
5
5
|
import argparse
|
|
6
6
|
import json
|
|
7
7
|
|
|
8
|
+
from arctx_cli.commands._targets import resolve_target_kind
|
|
8
9
|
from arctx_cli.context import (
|
|
9
10
|
resolve_run_id_from_args,
|
|
10
11
|
resolve_store,
|
|
@@ -15,12 +16,12 @@ from arctx_cli.append_batch import graph_counts, maybe_append_or_save
|
|
|
15
16
|
|
|
16
17
|
|
|
17
18
|
def add_parser(subparsers) -> argparse.ArgumentParser:
|
|
18
|
-
parser = subparsers.add_parser("cut", help="Cut a Node or
|
|
19
|
-
parser.add_argument("kind", nargs="?"
|
|
19
|
+
parser = subparsers.add_parser("cut", help="Cut a Node or Step")
|
|
20
|
+
parser.add_argument("kind", nargs="?")
|
|
20
21
|
parser.add_argument("id", nargs="?")
|
|
21
22
|
group = parser.add_mutually_exclusive_group(required=False)
|
|
22
23
|
group.add_argument("--node", dest="node_id", metavar="NODE_ID")
|
|
23
|
-
group.add_argument("--
|
|
24
|
+
group.add_argument("--step", dest="step_id", metavar="STEP_ID")
|
|
24
25
|
parser.add_argument("--run", default=None)
|
|
25
26
|
parser.add_argument("--reason", default=None)
|
|
26
27
|
parser.add_argument("--store-dir", default=None)
|
|
@@ -62,22 +63,34 @@ def run_cut_command(
|
|
|
62
63
|
|
|
63
64
|
|
|
64
65
|
def cli_cut(args) -> int:
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
target_id = args.id
|
|
69
|
-
target_kind = args.kind
|
|
70
|
-
elif args.node_id is not None:
|
|
66
|
+
run_id = resolve_run_id_from_args(args)
|
|
67
|
+
store_dir = args.store_dir
|
|
68
|
+
if args.node_id is not None:
|
|
71
69
|
target_id = args.node_id
|
|
72
70
|
target_kind = "node"
|
|
73
|
-
elif args.
|
|
74
|
-
target_id = args.
|
|
75
|
-
target_kind = "
|
|
71
|
+
elif args.step_id is not None:
|
|
72
|
+
target_id = args.step_id
|
|
73
|
+
target_kind = "step"
|
|
74
|
+
elif args.kind is not None and args.id is not None:
|
|
75
|
+
if args.kind not in ("node", "step", "step"):
|
|
76
|
+
raise ValueError("cut target kind must be node, step, or step")
|
|
77
|
+
target_id = args.id
|
|
78
|
+
target_kind = "step" if args.kind == "step" else args.kind
|
|
79
|
+
elif args.kind is not None:
|
|
80
|
+
target_id = args.kind
|
|
81
|
+
store = resolve_store(store_dir)
|
|
82
|
+
if not store.run_path(run_id).exists():
|
|
83
|
+
raise KeyError(f"unknown run_id: {run_id}")
|
|
84
|
+
handle = store.load_run(run_id)
|
|
85
|
+
resolved = resolve_target_kind(handle, target_id)
|
|
86
|
+
if resolved == "payload":
|
|
87
|
+
raise ValueError("cannot cut a payload")
|
|
88
|
+
target_kind = resolved
|
|
76
89
|
else:
|
|
77
|
-
raise ValueError("provide 'node <id>', '
|
|
90
|
+
raise ValueError("provide '<id>', 'node <id>', 'step <id>', --node, or --step")
|
|
78
91
|
|
|
79
92
|
result = run_cut_command(
|
|
80
|
-
run_id=
|
|
93
|
+
run_id=run_id,
|
|
81
94
|
target_id=target_id,
|
|
82
95
|
target_kind=target_kind,
|
|
83
96
|
reason=args.reason,
|
|
@@ -25,9 +25,9 @@ def add_parser(subparsers) -> argparse.ArgumentParser:
|
|
|
25
25
|
parser.add_argument("--depth", type=int, default=None,
|
|
26
26
|
help="Limit traversal depth")
|
|
27
27
|
parser.add_argument("--observed-only", action="store_true",
|
|
28
|
-
help="Hide predicted output
|
|
28
|
+
help="Hide predicted output steps")
|
|
29
29
|
parser.add_argument("--predicted-only", action="store_true",
|
|
30
|
-
help="Hide observed (result) output
|
|
30
|
+
help="Hide observed (result) output steps")
|
|
31
31
|
parser.add_argument("--full-payloads", action="store_true",
|
|
32
32
|
help="Include full payload metrics / rationale")
|
|
33
33
|
parser.add_argument("--run", default=None)
|
|
@@ -32,7 +32,7 @@ def add_parser(subparsers) -> argparse.ArgumentParser:
|
|
|
32
32
|
parser.add_argument("--full-payloads", action="store_true",
|
|
33
33
|
help="Include full payload content")
|
|
34
34
|
parser.add_argument("--exclude-cut", action="store_true",
|
|
35
|
-
help="Drop cut (inactive) nodes and
|
|
35
|
+
help="Drop cut (inactive) nodes and steps")
|
|
36
36
|
parser.add_argument("--include-local", action="store_true",
|
|
37
37
|
help="Keep repo local_path in the output (off by default)")
|
|
38
38
|
parser.add_argument("--output", "-o", default=None,
|
|
@@ -135,8 +135,9 @@ def run_init_command(
|
|
|
135
135
|
repo_root = find_repo_root()
|
|
136
136
|
write_arctx_id(repo_root, handle.run_id)
|
|
137
137
|
written_arctx_id_path = str(arctx_id_path(repo_root))
|
|
138
|
-
except RuntimeError:
|
|
139
|
-
# Not inside a git repo
|
|
138
|
+
except (OSError, RuntimeError):
|
|
139
|
+
# Not inside a git repo, or the gitdir is read-only in the current
|
|
140
|
+
# sandbox. The run itself is still valid without an active pointer.
|
|
140
141
|
pass
|
|
141
142
|
|
|
142
143
|
installed_hook_path: str | None = None
|