claw-code 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.
Files changed (110) hide show
  1. claw_code-0.2.0.dist-info/METADATA +560 -0
  2. claw_code-0.2.0.dist-info/RECORD +110 -0
  3. claw_code-0.2.0.dist-info/WHEEL +5 -0
  4. claw_code-0.2.0.dist-info/entry_points.txt +2 -0
  5. claw_code-0.2.0.dist-info/licenses/LICENSE +68 -0
  6. claw_code-0.2.0.dist-info/top_level.txt +1 -0
  7. src/QueryEngine.py +19 -0
  8. src/Tool.py +15 -0
  9. src/__init__.py +29 -0
  10. src/assistant/__init__.py +16 -0
  11. src/bootstrap/__init__.py +16 -0
  12. src/bootstrap_graph.py +27 -0
  13. src/bridge/__init__.py +16 -0
  14. src/buddy/__init__.py +16 -0
  15. src/cli/__init__.py +16 -0
  16. src/command_graph.py +34 -0
  17. src/commands.py +90 -0
  18. src/components/__init__.py +16 -0
  19. src/config.py +58 -0
  20. src/constants/__init__.py +16 -0
  21. src/context.py +47 -0
  22. src/coordinator/__init__.py +16 -0
  23. src/costHook.py +8 -0
  24. src/cost_tracker.py +13 -0
  25. src/deferred_init.py +31 -0
  26. src/dialogLaunchers.py +15 -0
  27. src/direct_modes.py +21 -0
  28. src/entrypoints/__init__.py +16 -0
  29. src/execution_registry.py +51 -0
  30. src/history.py +22 -0
  31. src/hooks/__init__.py +16 -0
  32. src/init_wizard.py +238 -0
  33. src/ink.py +6 -0
  34. src/interactiveHelpers.py +5 -0
  35. src/keybindings/__init__.py +16 -0
  36. src/main.py +274 -0
  37. src/memdir/__init__.py +16 -0
  38. src/migrations/__init__.py +16 -0
  39. src/model_detection.py +96 -0
  40. src/models.py +49 -0
  41. src/moreright/__init__.py +16 -0
  42. src/native_ts/__init__.py +16 -0
  43. src/outputStyles/__init__.py +16 -0
  44. src/parity_audit.py +138 -0
  45. src/permissions.py +20 -0
  46. src/plugins/__init__.py +16 -0
  47. src/port_manifest.py +52 -0
  48. src/prefetch.py +23 -0
  49. src/projectOnboardingState.py +10 -0
  50. src/query.py +13 -0
  51. src/query_engine.py +289 -0
  52. src/reference_data/__init__.py +1 -0
  53. src/reference_data/archive_surface_snapshot.json +63 -0
  54. src/reference_data/commands_snapshot.json +1037 -0
  55. src/reference_data/subsystems/assistant.json +8 -0
  56. src/reference_data/subsystems/bootstrap.json +8 -0
  57. src/reference_data/subsystems/bridge.json +32 -0
  58. src/reference_data/subsystems/buddy.json +13 -0
  59. src/reference_data/subsystems/cli.json +26 -0
  60. src/reference_data/subsystems/components.json +32 -0
  61. src/reference_data/subsystems/constants.json +28 -0
  62. src/reference_data/subsystems/coordinator.json +8 -0
  63. src/reference_data/subsystems/entrypoints.json +15 -0
  64. src/reference_data/subsystems/hooks.json +32 -0
  65. src/reference_data/subsystems/keybindings.json +21 -0
  66. src/reference_data/subsystems/memdir.json +15 -0
  67. src/reference_data/subsystems/migrations.json +18 -0
  68. src/reference_data/subsystems/moreright.json +8 -0
  69. src/reference_data/subsystems/native_ts.json +11 -0
  70. src/reference_data/subsystems/outputStyles.json +8 -0
  71. src/reference_data/subsystems/plugins.json +9 -0
  72. src/reference_data/subsystems/remote.json +11 -0
  73. src/reference_data/subsystems/schemas.json +8 -0
  74. src/reference_data/subsystems/screens.json +10 -0
  75. src/reference_data/subsystems/server.json +10 -0
  76. src/reference_data/subsystems/services.json +32 -0
  77. src/reference_data/subsystems/skills.json +27 -0
  78. src/reference_data/subsystems/state.json +13 -0
  79. src/reference_data/subsystems/types.json +18 -0
  80. src/reference_data/subsystems/upstreamproxy.json +9 -0
  81. src/reference_data/subsystems/utils.json +32 -0
  82. src/reference_data/subsystems/vim.json +12 -0
  83. src/reference_data/subsystems/voice.json +8 -0
  84. src/reference_data/tools_snapshot.json +922 -0
  85. src/remote/__init__.py +16 -0
  86. src/remote_runtime.py +25 -0
  87. src/repl.py +577 -0
  88. src/replLauncher.py +5 -0
  89. src/runtime.py +205 -0
  90. src/schemas/__init__.py +16 -0
  91. src/screens/__init__.py +16 -0
  92. src/server/__init__.py +16 -0
  93. src/services/__init__.py +16 -0
  94. src/services/ollama_adapter.py +251 -0
  95. src/services/ollama_setup.py +192 -0
  96. src/session_store.py +79 -0
  97. src/setup.py +77 -0
  98. src/skills/__init__.py +16 -0
  99. src/state/__init__.py +16 -0
  100. src/system_init.py +23 -0
  101. src/task.py +5 -0
  102. src/tasks.py +11 -0
  103. src/tool_pool.py +37 -0
  104. src/tools.py +96 -0
  105. src/transcript.py +23 -0
  106. src/types/__init__.py +16 -0
  107. src/upstreamproxy/__init__.py +16 -0
  108. src/utils/__init__.py +16 -0
  109. src/vim/__init__.py +16 -0
  110. src/voice/__init__.py +16 -0
@@ -0,0 +1,51 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+
5
+ from .commands import PORTED_COMMANDS, execute_command
6
+ from .tools import PORTED_TOOLS, execute_tool
7
+
8
+
9
+ @dataclass(frozen=True)
10
+ class MirroredCommand:
11
+ name: str
12
+ source_hint: str
13
+
14
+ def execute(self, prompt: str) -> str:
15
+ return execute_command(self.name, prompt).message
16
+
17
+
18
+ @dataclass(frozen=True)
19
+ class MirroredTool:
20
+ name: str
21
+ source_hint: str
22
+
23
+ def execute(self, payload: str) -> str:
24
+ return execute_tool(self.name, payload).message
25
+
26
+
27
+ @dataclass(frozen=True)
28
+ class ExecutionRegistry:
29
+ commands: tuple[MirroredCommand, ...]
30
+ tools: tuple[MirroredTool, ...]
31
+
32
+ def command(self, name: str) -> MirroredCommand | None:
33
+ lowered = name.lower()
34
+ for command in self.commands:
35
+ if command.name.lower() == lowered:
36
+ return command
37
+ return None
38
+
39
+ def tool(self, name: str) -> MirroredTool | None:
40
+ lowered = name.lower()
41
+ for tool in self.tools:
42
+ if tool.name.lower() == lowered:
43
+ return tool
44
+ return None
45
+
46
+
47
+ def build_execution_registry() -> ExecutionRegistry:
48
+ return ExecutionRegistry(
49
+ commands=tuple(MirroredCommand(module.name, module.source_hint) for module in PORTED_COMMANDS),
50
+ tools=tuple(MirroredTool(module.name, module.source_hint) for module in PORTED_TOOLS),
51
+ )
src/history.py ADDED
@@ -0,0 +1,22 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass, field
4
+
5
+
6
+ @dataclass(frozen=True)
7
+ class HistoryEvent:
8
+ title: str
9
+ detail: str
10
+
11
+
12
+ @dataclass
13
+ class HistoryLog:
14
+ events: list[HistoryEvent] = field(default_factory=list)
15
+
16
+ def add(self, title: str, detail: str) -> None:
17
+ self.events.append(HistoryEvent(title=title, detail=detail))
18
+
19
+ def as_markdown(self) -> str:
20
+ lines = ['# Session History', '']
21
+ lines.extend(f'- {event.title}: {event.detail}' for event in self.events)
22
+ return '\n'.join(lines)
src/hooks/__init__.py ADDED
@@ -0,0 +1,16 @@
1
+ """Python package placeholder for the archived `hooks` subsystem."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from pathlib import Path
7
+
8
+ SNAPSHOT_PATH = Path(__file__).resolve().parent.parent / 'reference_data' / 'subsystems' / 'hooks.json'
9
+ _SNAPSHOT = json.loads(SNAPSHOT_PATH.read_text())
10
+
11
+ ARCHIVE_NAME = _SNAPSHOT['archive_name']
12
+ MODULE_COUNT = _SNAPSHOT['module_count']
13
+ SAMPLE_FILES = tuple(_SNAPSHOT['sample_files'])
14
+ PORTING_NOTE = f"Python placeholder package for '{ARCHIVE_NAME}' with {MODULE_COUNT} archived module references."
15
+
16
+ __all__ = ['ARCHIVE_NAME', 'MODULE_COUNT', 'PORTING_NOTE', 'SAMPLE_FILES']
src/init_wizard.py ADDED
@@ -0,0 +1,238 @@
1
+ """
2
+ First-run wizard for Claw Code initialization.
3
+ Detects Ollama, RAM, pulls model, writes config.
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ import logging
9
+ import sys
10
+ from pathlib import Path
11
+
12
+ from .config import load_config
13
+ from .services.ollama_adapter import OllamaAdapter
14
+ from .services.ollama_setup import (
15
+ check_ollama_installed,
16
+ ensure_config_exists,
17
+ pull_model,
18
+ validate_setup,
19
+ print_validation_report,
20
+ )
21
+ from .model_detection import get_available_models, detect_best_model
22
+
23
+
24
+ logger = logging.getLogger(__name__)
25
+
26
+
27
+ def print_banner():
28
+ """Print welcome banner"""
29
+ print("\n" + "=" * 70)
30
+ print(" 🚀 Welcome to Claw Code - Local Claude Alternative")
31
+ print("=" * 70)
32
+ print("\nThis wizard will help you get started in under 3 minutes.\n")
33
+
34
+
35
+ def step_1_check_ollama():
36
+ """Step 1: Verify Ollama installation"""
37
+ print("📋 Step 1: Checking Ollama installation...")
38
+
39
+ if check_ollama_installed():
40
+ print("✓ Ollama CLI found\n")
41
+ return True
42
+ else:
43
+ print("✗ Ollama not installed")
44
+ print("\n→ Download from: https://ollama.ai")
45
+ print("→ Then run: claw-code init\n")
46
+ return False
47
+
48
+
49
+ def step_2_detect_system():
50
+ """Step 2: Detect system VRAM"""
51
+ print("📋 Step 2: Detecting system resources...")
52
+
53
+ vram_gb = OllamaAdapter.get_available_vram_gb()
54
+ print(f"✓ Detected: {vram_gb:.1f}GB VRAM\n")
55
+
56
+ return vram_gb
57
+
58
+
59
+ def step_3_recommend_model(vram_gb: float):
60
+ """Step 3: Recommend model tier"""
61
+ print("📋 Step 3: Recommending model for your system...")
62
+
63
+ from .services.ollama_adapter import MODEL_TIERS
64
+
65
+ for tier in MODEL_TIERS:
66
+ if tier.vram_min_gb <= vram_gb <= tier.vram_max_gb:
67
+ print(f"✓ Recommended: {tier.name}")
68
+ print(f" VRAM: {tier.vram_required_gb}GB | Speed: {tier.tokens_per_sec_estimate} tok/s")
69
+ print(f" Use: {tier.use_case}\n")
70
+ return tier.name
71
+
72
+ # Fallback
73
+ print("✓ Using conservative tier: phi4-mini\n")
74
+ return "phi4-mini"
75
+
76
+
77
+ def step_4_check_models():
78
+ """Step 4: Check if model is already available"""
79
+ print("📋 Step 4: Checking for available models...")
80
+
81
+ try:
82
+ available = get_available_models()
83
+ if available:
84
+ print(f"✓ Found {len(available)} model(s) installed:")
85
+ for model in available:
86
+ print(f" - {model}")
87
+ print()
88
+ return available
89
+ else:
90
+ print("⚠ No models installed yet\n")
91
+ return None
92
+ except Exception as e:
93
+ logger.warning(f"Could not list models: {e}")
94
+ return None
95
+
96
+
97
+ def step_5_pull_model(recommended_model: str, available_models: list[str] | None):
98
+ """Step 5: Pull model if needed"""
99
+ print("📋 Step 5: Model setup...")
100
+
101
+ # Check if recommended is already available
102
+ if available_models and recommended_model in available_models:
103
+ print(f"✓ {recommended_model} already installed\n")
104
+ return recommended_model
105
+
106
+ print(f"→ Need to download {recommended_model} (~6GB)")
107
+ print("→ This might take a few minutes...\n")
108
+
109
+ response = input("Pull now? (y/n): ").strip().lower()
110
+
111
+ if response == "y":
112
+ print()
113
+ if pull_model(recommended_model):
114
+ print(f"\n✓ {recommended_model} ready\n")
115
+ return recommended_model
116
+ else:
117
+ print(f"\n✗ Failed to pull {recommended_model}")
118
+ print("→ Try manually: ollama pull " + recommended_model)
119
+ return None
120
+ else:
121
+ print(f"\nℹ To pull manually: ollama pull {recommended_model}\n")
122
+ return None
123
+
124
+
125
+ def step_6_start_ollama():
126
+ """Step 6: Instruct to start Ollama if not running"""
127
+ print("📋 Step 6: Checking if Ollama is running...")
128
+
129
+ try:
130
+ OllamaAdapter()
131
+ print("✓ Ollama server is running\n")
132
+ return True
133
+ except RuntimeError:
134
+ print("⚠ Ollama server not running")
135
+ print("\n→ In another terminal, run: ollama serve\n")
136
+ return False
137
+
138
+
139
+ def step_7_create_config():
140
+ """Step 7: Create configuration file"""
141
+ print("📋 Step 7: Creating configuration...")
142
+
143
+ config_path = ensure_config_exists()
144
+ print(f"✓ Config created: {config_path}\n")
145
+
146
+ return config_path
147
+
148
+
149
+ def step_8_validate():
150
+ """Step 8: Validate complete setup"""
151
+ print("📋 Step 8: Validating setup...")
152
+
153
+ report = validate_setup()
154
+
155
+ if report["ollama_running"] and report["model_available"]:
156
+ print(f"✓ Everything ready! Model: {report['model_available']}\n")
157
+ return True
158
+ else:
159
+ if not report["ollama_running"]:
160
+ print("⚠ Ollama server not detected (start with: ollama serve)")
161
+ if not report["model_available"]:
162
+ print("⚠ No model detected (pull with: ollama pull qwen2.5-coder:7b)")
163
+ print()
164
+ return False
165
+
166
+
167
+ def print_next_steps():
168
+ """Print what to do next"""
169
+ print("=" * 70)
170
+ print(" 🎉 Setup Complete!")
171
+ print("=" * 70 + "\n")
172
+
173
+ print("Next steps:\n")
174
+ print("1. Make sure Ollama is running (in another terminal):")
175
+ print(" ollama serve\n")
176
+
177
+ print("2. Start Claw Code interactive REPL:")
178
+ print(" claw-code\n")
179
+
180
+ print("3. Try some commands:")
181
+ print(" > write a Python quicksort")
182
+ print(" > explain this function")
183
+ print(" > help\n")
184
+
185
+ print("Commands:")
186
+ print(" /clear — Clear conversation history")
187
+ print(" /exit — Exit the REPL")
188
+ print(" /session — Show session info")
189
+ print(" /help — Show help\n")
190
+
191
+
192
+ def run_init_wizard() -> bool:
193
+ """Run the complete initialization wizard"""
194
+ print_banner()
195
+
196
+ # Step 1: Check Ollama
197
+ if not step_1_check_ollama():
198
+ return False
199
+
200
+ # Step 2: Detect system
201
+ vram_gb = step_2_detect_system()
202
+
203
+ # Step 3: Recommend model
204
+ recommended = step_3_recommend_model(vram_gb)
205
+
206
+ # Step 4: Check available models
207
+ available = step_4_check_models()
208
+
209
+ # Step 5: Pull model if needed
210
+ model = step_5_pull_model(recommended, available)
211
+
212
+ # Step 6: Check if Ollama is running
213
+ ollama_running = step_6_start_ollama()
214
+
215
+ # Step 7: Create config
216
+ step_7_create_config()
217
+
218
+ # Step 8: Validate
219
+ ready = step_8_validate()
220
+
221
+ # Next steps
222
+ print_next_steps()
223
+
224
+ if not ollama_running:
225
+ print("⚠ Please start Ollama (ollama serve) in another terminal,")
226
+ print(" then run: claw-code\n")
227
+ elif not model:
228
+ print("⚠ Please pull a model manually, then run: claw-code\n")
229
+ else:
230
+ print("✓ Ready to go! Run: claw-code\n")
231
+
232
+ return ready
233
+
234
+
235
+ if __name__ == "__main__":
236
+ logging.basicConfig(level=logging.INFO)
237
+ success = run_init_wizard()
238
+ sys.exit(0 if success else 1)
src/ink.py ADDED
@@ -0,0 +1,6 @@
1
+ from __future__ import annotations
2
+
3
+
4
+ def render_markdown_panel(text: str) -> str:
5
+ border = '=' * 40
6
+ return f"{border}\n{text}\n{border}"
@@ -0,0 +1,5 @@
1
+ from __future__ import annotations
2
+
3
+
4
+ def bulletize(items: list[str]) -> str:
5
+ return '\n'.join(f'- {item}' for item in items)
@@ -0,0 +1,16 @@
1
+ """Python package placeholder for the archived `keybindings` subsystem."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from pathlib import Path
7
+
8
+ SNAPSHOT_PATH = Path(__file__).resolve().parent.parent / 'reference_data' / 'subsystems' / 'keybindings.json'
9
+ _SNAPSHOT = json.loads(SNAPSHOT_PATH.read_text())
10
+
11
+ ARCHIVE_NAME = _SNAPSHOT['archive_name']
12
+ MODULE_COUNT = _SNAPSHOT['module_count']
13
+ SAMPLE_FILES = tuple(_SNAPSHOT['sample_files'])
14
+ PORTING_NOTE = f"Python placeholder package for '{ARCHIVE_NAME}' with {MODULE_COUNT} archived module references."
15
+
16
+ __all__ = ['ARCHIVE_NAME', 'MODULE_COUNT', 'PORTING_NOTE', 'SAMPLE_FILES']
src/main.py ADDED
@@ -0,0 +1,274 @@
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+
5
+ from .bootstrap_graph import build_bootstrap_graph
6
+ from .command_graph import build_command_graph
7
+ from .commands import execute_command, get_command, get_commands, render_command_index
8
+ from .direct_modes import run_deep_link, run_direct_connect
9
+ from .init_wizard import run_init_wizard
10
+ from .parity_audit import run_parity_audit
11
+ from .permissions import ToolPermissionContext
12
+ from .port_manifest import build_port_manifest
13
+ from .query_engine import QueryEnginePort
14
+ from .remote_runtime import run_remote_mode, run_ssh_mode, run_teleport_mode
15
+ from .repl import run_repl
16
+ from .runtime import PortRuntime
17
+ from .session_store import load_session
18
+ from .setup import run_setup
19
+ from .tool_pool import assemble_tool_pool
20
+ from .tools import execute_tool, get_tool, get_tools, render_tool_index
21
+
22
+
23
+ def build_parser() -> argparse.ArgumentParser:
24
+ parser = argparse.ArgumentParser(description='Claw Code - Local Claude AI CLI powered by Ollama')
25
+
26
+ # Top-level flags
27
+ parser.add_argument('--version', action='store_true', help='show version and exit')
28
+ parser.add_argument('--resume', type=str, help='resume a previous session by ID')
29
+
30
+ subparsers = parser.add_subparsers(dest='command', required=False)
31
+
32
+ # Initialization wizard
33
+ subparsers.add_parser('init', help='first-run setup wizard (checks Ollama, pulls model)')
34
+
35
+ # Interactive REPL (implicit if no command)
36
+
37
+ # Existing commands
38
+ subparsers.add_parser('summary', help='render a Markdown summary of the Python porting workspace')
39
+ subparsers.add_parser('manifest', help='print the current Python workspace manifest')
40
+ subparsers.add_parser('parity-audit', help='compare the Python workspace against the local ignored TypeScript archive when available')
41
+ subparsers.add_parser('setup-report', help='render the startup/prefetch setup report')
42
+ subparsers.add_parser('command-graph', help='show command graph segmentation')
43
+ subparsers.add_parser('tool-pool', help='show assembled tool pool with default settings')
44
+ subparsers.add_parser('bootstrap-graph', help='show the mirrored bootstrap/runtime graph stages')
45
+ list_parser = subparsers.add_parser('subsystems', help='list the current Python modules in the workspace')
46
+ list_parser.add_argument('--limit', type=int, default=32)
47
+
48
+ commands_parser = subparsers.add_parser('commands', help='list mirrored command entries from the archived snapshot')
49
+ commands_parser.add_argument('--limit', type=int, default=20)
50
+ commands_parser.add_argument('--query')
51
+ commands_parser.add_argument('--no-plugin-commands', action='store_true')
52
+ commands_parser.add_argument('--no-skill-commands', action='store_true')
53
+
54
+ tools_parser = subparsers.add_parser('tools', help='list mirrored tool entries from the archived snapshot')
55
+ tools_parser.add_argument('--limit', type=int, default=20)
56
+ tools_parser.add_argument('--query')
57
+ tools_parser.add_argument('--simple-mode', action='store_true')
58
+ tools_parser.add_argument('--no-mcp', action='store_true')
59
+ tools_parser.add_argument('--deny-tool', action='append', default=[])
60
+ tools_parser.add_argument('--deny-prefix', action='append', default=[])
61
+
62
+ route_parser = subparsers.add_parser('route', help='route a prompt across mirrored command/tool inventories')
63
+ route_parser.add_argument('prompt')
64
+ route_parser.add_argument('--limit', type=int, default=5)
65
+
66
+ bootstrap_parser = subparsers.add_parser('bootstrap', help='build a runtime-style session report from the mirrored inventories')
67
+ bootstrap_parser.add_argument('prompt')
68
+ bootstrap_parser.add_argument('--limit', type=int, default=5)
69
+
70
+ loop_parser = subparsers.add_parser('turn-loop', help='run a small stateful turn loop for the mirrored runtime')
71
+ loop_parser.add_argument('prompt')
72
+ loop_parser.add_argument('--limit', type=int, default=5)
73
+ loop_parser.add_argument('--max-turns', type=int, default=3)
74
+ loop_parser.add_argument('--structured-output', action='store_true')
75
+ loop_parser.add_argument('--stream', action='store_true', help='stream output in real-time from Ollama')
76
+
77
+ flush_parser = subparsers.add_parser('flush-transcript', help='persist and flush a temporary session transcript')
78
+ flush_parser.add_argument('prompt')
79
+
80
+ load_session_parser = subparsers.add_parser('load-session', help='load a previously persisted session')
81
+ load_session_parser.add_argument('session_id')
82
+
83
+ remote_parser = subparsers.add_parser('remote-mode', help='simulate remote-control runtime branching')
84
+ remote_parser.add_argument('target')
85
+ ssh_parser = subparsers.add_parser('ssh-mode', help='simulate SSH runtime branching')
86
+ ssh_parser.add_argument('target')
87
+ teleport_parser = subparsers.add_parser('teleport-mode', help='simulate teleport runtime branching')
88
+ teleport_parser.add_argument('target')
89
+ direct_parser = subparsers.add_parser('direct-connect-mode', help='simulate direct-connect runtime branching')
90
+ direct_parser.add_argument('target')
91
+ deep_link_parser = subparsers.add_parser('deep-link-mode', help='simulate deep-link runtime branching')
92
+ deep_link_parser.add_argument('target')
93
+
94
+ show_command = subparsers.add_parser('show-command', help='show one mirrored command entry by exact name')
95
+ show_command.add_argument('name')
96
+ show_tool = subparsers.add_parser('show-tool', help='show one mirrored tool entry by exact name')
97
+ show_tool.add_argument('name')
98
+
99
+ exec_command_parser = subparsers.add_parser('exec-command', help='execute a mirrored command shim by exact name')
100
+ exec_command_parser.add_argument('name')
101
+ exec_command_parser.add_argument('prompt')
102
+
103
+ exec_tool_parser = subparsers.add_parser('exec-tool', help='execute a mirrored tool shim by exact name')
104
+ exec_tool_parser.add_argument('name')
105
+ exec_tool_parser.add_argument('payload')
106
+ return parser
107
+
108
+
109
+ def main(argv: list[str] | None = None) -> int:
110
+ parser = build_parser()
111
+ args = parser.parse_args(argv)
112
+
113
+ # Handle --version
114
+ if args.version:
115
+ print("claw-code v0.1.0 (Phase 1)")
116
+ return 0
117
+
118
+ # Handle init command
119
+ if args.command == 'init':
120
+ success = run_init_wizard()
121
+ return 0 if success else 1
122
+
123
+ # Handle --resume flag
124
+ if args.resume:
125
+ print(f"Resuming session {args.resume}...")
126
+ try:
127
+ engine = QueryEnginePort.from_saved_session(args.resume)
128
+ print(f"Loaded {len(engine.mutable_messages)} messages from session")
129
+ # TODO: Drop into REPL with loaded session
130
+ print("⚠ Full session resumption coming in Phase 2.5")
131
+ return 0
132
+ except Exception as e:
133
+ print(f"✗ Failed to load session: {e}")
134
+ return 1
135
+
136
+ # Default to REPL if no command
137
+ if not args.command:
138
+ run_repl()
139
+ return 0
140
+
141
+ # Original command handling below
142
+ manifest = build_port_manifest()
143
+ if args.command == 'summary':
144
+ print(QueryEnginePort(manifest).render_summary())
145
+ return 0
146
+ if args.command == 'manifest':
147
+ print(manifest.to_markdown())
148
+ return 0
149
+ if args.command == 'parity-audit':
150
+ print(run_parity_audit().to_markdown())
151
+ return 0
152
+ if args.command == 'setup-report':
153
+ print(run_setup().as_markdown())
154
+ return 0
155
+ if args.command == 'command-graph':
156
+ print(build_command_graph().as_markdown())
157
+ return 0
158
+ if args.command == 'tool-pool':
159
+ print(assemble_tool_pool().as_markdown())
160
+ return 0
161
+ if args.command == 'bootstrap-graph':
162
+ print(build_bootstrap_graph().as_markdown())
163
+ return 0
164
+ if args.command == 'subsystems':
165
+ for subsystem in manifest.top_level_modules[: args.limit]:
166
+ print(f'{subsystem.name}\t{subsystem.file_count}\t{subsystem.notes}')
167
+ return 0
168
+ if args.command == 'commands':
169
+ if args.query:
170
+ print(render_command_index(limit=args.limit, query=args.query))
171
+ else:
172
+ commands = get_commands(include_plugin_commands=not args.no_plugin_commands, include_skill_commands=not args.no_skill_commands)
173
+ output_lines = [f'Command entries: {len(commands)}', '']
174
+ output_lines.extend(f'- {module.name} — {module.source_hint}' for module in commands[: args.limit])
175
+ print('\n'.join(output_lines))
176
+ return 0
177
+ if args.command == 'tools':
178
+ if args.query:
179
+ print(render_tool_index(limit=args.limit, query=args.query))
180
+ else:
181
+ permission_context = ToolPermissionContext.from_iterables(args.deny_tool, args.deny_prefix)
182
+ tools = get_tools(simple_mode=args.simple_mode, include_mcp=not args.no_mcp, permission_context=permission_context)
183
+ output_lines = [f'Tool entries: {len(tools)}', '']
184
+ output_lines.extend(f'- {module.name} — {module.source_hint}' for module in tools[: args.limit])
185
+ print('\n'.join(output_lines))
186
+ return 0
187
+ if args.command == 'route':
188
+ matches = PortRuntime().route_prompt(args.prompt, limit=args.limit)
189
+ if not matches:
190
+ print('No mirrored command/tool matches found.')
191
+ return 0
192
+ for match in matches:
193
+ print(f'{match.kind}\t{match.name}\t{match.score}\t{match.source_hint}')
194
+ return 0
195
+ if args.command == 'bootstrap':
196
+ print(PortRuntime().bootstrap_session(args.prompt, limit=args.limit).as_markdown())
197
+ return 0
198
+ if args.command == 'turn-loop':
199
+ if hasattr(args, 'stream') and args.stream:
200
+ # Stream mode: real-time output
201
+ runtime = PortRuntime()
202
+ for event in runtime.stream_turn_loop(args.prompt, limit=args.limit, max_turns=args.max_turns, structured_output=args.structured_output):
203
+ if event['type'] == 'message_delta':
204
+ print(event['text'], end='', flush=True)
205
+ elif event['type'] == 'message_stop':
206
+ print(f"\n\n[stop_reason={event['stop_reason']}]")
207
+ else:
208
+ # Buffered mode: collect and display
209
+ results = PortRuntime().run_turn_loop(args.prompt, limit=args.limit, max_turns=args.max_turns, structured_output=args.structured_output)
210
+ for idx, result in enumerate(results, start=1):
211
+ print(f'## Turn {idx}')
212
+ print(result.output)
213
+ print(f'stop_reason={result.stop_reason}')
214
+ return 0
215
+ if args.command == 'flush-transcript':
216
+ engine = QueryEnginePort.from_workspace()
217
+ engine.submit_message(args.prompt)
218
+ path = engine.persist_session()
219
+ print(path)
220
+ print(f'flushed={engine.transcript_store.flushed}')
221
+ return 0
222
+ if args.command == 'load-session':
223
+ session = load_session(args.session_id)
224
+ print(f'{session.session_id}\n{len(session.messages)} messages\nin={session.input_tokens} out={session.output_tokens}')
225
+ return 0
226
+ if args.command == 'remote-mode':
227
+ print(run_remote_mode(args.target).as_text())
228
+ return 0
229
+ if args.command == 'ssh-mode':
230
+ print(run_ssh_mode(args.target).as_text())
231
+ return 0
232
+ if args.command == 'teleport-mode':
233
+ print(run_teleport_mode(args.target).as_text())
234
+ return 0
235
+ if args.command == 'direct-connect-mode':
236
+ print(run_direct_connect(args.target).as_text())
237
+ return 0
238
+ if args.command == 'deep-link-mode':
239
+ print(run_deep_link(args.target).as_text())
240
+ return 0
241
+ if args.command == 'show-command':
242
+ module = get_command(args.name)
243
+ if module is None:
244
+ print(f'Command not found: {args.name}')
245
+ return 1
246
+ print('\n'.join([module.name, module.source_hint, module.responsibility]))
247
+ return 0
248
+ if args.command == 'show-tool':
249
+ module = get_tool(args.name)
250
+ if module is None:
251
+ print(f'Tool not found: {args.name}')
252
+ return 1
253
+ print('\n'.join([module.name, module.source_hint, module.responsibility]))
254
+ return 0
255
+ if args.command == 'exec-command':
256
+ result = execute_command(args.name, args.prompt)
257
+ print(result.message)
258
+ return 0 if result.handled else 1
259
+ if args.command == 'exec-tool':
260
+ result = execute_tool(args.name, args.payload)
261
+ print(result.message)
262
+ return 0 if result.handled else 1
263
+ parser.error(f'unknown command: {args.command}')
264
+ return 2
265
+
266
+
267
+ def cli_entry() -> None:
268
+ """Entry point for pip-installed 'claw-code' command"""
269
+ import sys
270
+ sys.exit(main())
271
+
272
+
273
+ if __name__ == '__main__':
274
+ raise SystemExit(main())
src/memdir/__init__.py ADDED
@@ -0,0 +1,16 @@
1
+ """Python package placeholder for the archived `memdir` subsystem."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from pathlib import Path
7
+
8
+ SNAPSHOT_PATH = Path(__file__).resolve().parent.parent / 'reference_data' / 'subsystems' / 'memdir.json'
9
+ _SNAPSHOT = json.loads(SNAPSHOT_PATH.read_text())
10
+
11
+ ARCHIVE_NAME = _SNAPSHOT['archive_name']
12
+ MODULE_COUNT = _SNAPSHOT['module_count']
13
+ SAMPLE_FILES = tuple(_SNAPSHOT['sample_files'])
14
+ PORTING_NOTE = f"Python placeholder package for '{ARCHIVE_NAME}' with {MODULE_COUNT} archived module references."
15
+
16
+ __all__ = ['ARCHIVE_NAME', 'MODULE_COUNT', 'PORTING_NOTE', 'SAMPLE_FILES']
@@ -0,0 +1,16 @@
1
+ """Python package placeholder for the archived `migrations` subsystem."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from pathlib import Path
7
+
8
+ SNAPSHOT_PATH = Path(__file__).resolve().parent.parent / 'reference_data' / 'subsystems' / 'migrations.json'
9
+ _SNAPSHOT = json.loads(SNAPSHOT_PATH.read_text())
10
+
11
+ ARCHIVE_NAME = _SNAPSHOT['archive_name']
12
+ MODULE_COUNT = _SNAPSHOT['module_count']
13
+ SAMPLE_FILES = tuple(_SNAPSHOT['sample_files'])
14
+ PORTING_NOTE = f"Python placeholder package for '{ARCHIVE_NAME}' with {MODULE_COUNT} archived module references."
15
+
16
+ __all__ = ['ARCHIVE_NAME', 'MODULE_COUNT', 'PORTING_NOTE', 'SAMPLE_FILES']