main.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. from __future__ import annotations
  2. import argparse
  3. from .bootstrap_graph import build_bootstrap_graph
  4. from .command_graph import build_command_graph
  5. from .commands import execute_command, get_command, get_commands, render_command_index
  6. from .direct_modes import run_deep_link, run_direct_connect
  7. from .parity_audit import run_parity_audit
  8. from .permissions import ToolPermissionContext
  9. from .port_manifest import build_port_manifest
  10. from .query_engine import QueryEnginePort
  11. from .remote_runtime import run_remote_mode, run_ssh_mode, run_teleport_mode
  12. from .runtime import PortRuntime
  13. from .session_store import load_session
  14. from .setup import run_setup
  15. from .tool_pool import assemble_tool_pool
  16. from .tools import execute_tool, get_tool, get_tools, render_tool_index
  17. def build_parser() -> argparse.ArgumentParser:
  18. parser = argparse.ArgumentParser(description='Python porting workspace for the Claude Code rewrite effort')
  19. subparsers = parser.add_subparsers(dest='command', required=True)
  20. subparsers.add_parser('summary', help='render a Markdown summary of the Python porting workspace')
  21. subparsers.add_parser('manifest', help='print the current Python workspace manifest')
  22. subparsers.add_parser('parity-audit', help='compare the Python workspace against the local ignored TypeScript archive when available')
  23. subparsers.add_parser('setup-report', help='render the startup/prefetch setup report')
  24. subparsers.add_parser('command-graph', help='show command graph segmentation')
  25. subparsers.add_parser('tool-pool', help='show assembled tool pool with default settings')
  26. subparsers.add_parser('bootstrap-graph', help='show the mirrored bootstrap/runtime graph stages')
  27. list_parser = subparsers.add_parser('subsystems', help='list the current Python modules in the workspace')
  28. list_parser.add_argument('--limit', type=int, default=32)
  29. commands_parser = subparsers.add_parser('commands', help='list mirrored command entries from the archived snapshot')
  30. commands_parser.add_argument('--limit', type=int, default=20)
  31. commands_parser.add_argument('--query')
  32. commands_parser.add_argument('--no-plugin-commands', action='store_true')
  33. commands_parser.add_argument('--no-skill-commands', action='store_true')
  34. tools_parser = subparsers.add_parser('tools', help='list mirrored tool entries from the archived snapshot')
  35. tools_parser.add_argument('--limit', type=int, default=20)
  36. tools_parser.add_argument('--query')
  37. tools_parser.add_argument('--simple-mode', action='store_true')
  38. tools_parser.add_argument('--no-mcp', action='store_true')
  39. tools_parser.add_argument('--deny-tool', action='append', default=[])
  40. tools_parser.add_argument('--deny-prefix', action='append', default=[])
  41. route_parser = subparsers.add_parser('route', help='route a prompt across mirrored command/tool inventories')
  42. route_parser.add_argument('prompt')
  43. route_parser.add_argument('--limit', type=int, default=5)
  44. bootstrap_parser = subparsers.add_parser('bootstrap', help='build a runtime-style session report from the mirrored inventories')
  45. bootstrap_parser.add_argument('prompt')
  46. bootstrap_parser.add_argument('--limit', type=int, default=5)
  47. loop_parser = subparsers.add_parser('turn-loop', help='run a small stateful turn loop for the mirrored runtime')
  48. loop_parser.add_argument('prompt')
  49. loop_parser.add_argument('--limit', type=int, default=5)
  50. loop_parser.add_argument('--max-turns', type=int, default=3)
  51. loop_parser.add_argument('--structured-output', action='store_true')
  52. flush_parser = subparsers.add_parser('flush-transcript', help='persist and flush a temporary session transcript')
  53. flush_parser.add_argument('prompt')
  54. load_session_parser = subparsers.add_parser('load-session', help='load a previously persisted session')
  55. load_session_parser.add_argument('session_id')
  56. remote_parser = subparsers.add_parser('remote-mode', help='simulate remote-control runtime branching')
  57. remote_parser.add_argument('target')
  58. ssh_parser = subparsers.add_parser('ssh-mode', help='simulate SSH runtime branching')
  59. ssh_parser.add_argument('target')
  60. teleport_parser = subparsers.add_parser('teleport-mode', help='simulate teleport runtime branching')
  61. teleport_parser.add_argument('target')
  62. direct_parser = subparsers.add_parser('direct-connect-mode', help='simulate direct-connect runtime branching')
  63. direct_parser.add_argument('target')
  64. deep_link_parser = subparsers.add_parser('deep-link-mode', help='simulate deep-link runtime branching')
  65. deep_link_parser.add_argument('target')
  66. show_command = subparsers.add_parser('show-command', help='show one mirrored command entry by exact name')
  67. show_command.add_argument('name')
  68. show_tool = subparsers.add_parser('show-tool', help='show one mirrored tool entry by exact name')
  69. show_tool.add_argument('name')
  70. exec_command_parser = subparsers.add_parser('exec-command', help='execute a mirrored command shim by exact name')
  71. exec_command_parser.add_argument('name')
  72. exec_command_parser.add_argument('prompt')
  73. exec_tool_parser = subparsers.add_parser('exec-tool', help='execute a mirrored tool shim by exact name')
  74. exec_tool_parser.add_argument('name')
  75. exec_tool_parser.add_argument('payload')
  76. return parser
  77. def main(argv: list[str] | None = None) -> int:
  78. parser = build_parser()
  79. args = parser.parse_args(argv)
  80. manifest = build_port_manifest()
  81. if args.command == 'summary':
  82. print(QueryEnginePort(manifest).render_summary())
  83. return 0
  84. if args.command == 'manifest':
  85. print(manifest.to_markdown())
  86. return 0
  87. if args.command == 'parity-audit':
  88. print(run_parity_audit().to_markdown())
  89. return 0
  90. if args.command == 'setup-report':
  91. print(run_setup().as_markdown())
  92. return 0
  93. if args.command == 'command-graph':
  94. print(build_command_graph().as_markdown())
  95. return 0
  96. if args.command == 'tool-pool':
  97. print(assemble_tool_pool().as_markdown())
  98. return 0
  99. if args.command == 'bootstrap-graph':
  100. print(build_bootstrap_graph().as_markdown())
  101. return 0
  102. if args.command == 'subsystems':
  103. for subsystem in manifest.top_level_modules[: args.limit]:
  104. print(f'{subsystem.name}\t{subsystem.file_count}\t{subsystem.notes}')
  105. return 0
  106. if args.command == 'commands':
  107. if args.query:
  108. print(render_command_index(limit=args.limit, query=args.query))
  109. else:
  110. commands = get_commands(include_plugin_commands=not args.no_plugin_commands, include_skill_commands=not args.no_skill_commands)
  111. output_lines = [f'Command entries: {len(commands)}', '']
  112. output_lines.extend(f'- {module.name} — {module.source_hint}' for module in commands[: args.limit])
  113. print('\n'.join(output_lines))
  114. return 0
  115. if args.command == 'tools':
  116. if args.query:
  117. print(render_tool_index(limit=args.limit, query=args.query))
  118. else:
  119. permission_context = ToolPermissionContext.from_iterables(args.deny_tool, args.deny_prefix)
  120. tools = get_tools(simple_mode=args.simple_mode, include_mcp=not args.no_mcp, permission_context=permission_context)
  121. output_lines = [f'Tool entries: {len(tools)}', '']
  122. output_lines.extend(f'- {module.name} — {module.source_hint}' for module in tools[: args.limit])
  123. print('\n'.join(output_lines))
  124. return 0
  125. if args.command == 'route':
  126. matches = PortRuntime().route_prompt(args.prompt, limit=args.limit)
  127. if not matches:
  128. print('No mirrored command/tool matches found.')
  129. return 0
  130. for match in matches:
  131. print(f'{match.kind}\t{match.name}\t{match.score}\t{match.source_hint}')
  132. return 0
  133. if args.command == 'bootstrap':
  134. print(PortRuntime().bootstrap_session(args.prompt, limit=args.limit).as_markdown())
  135. return 0
  136. if args.command == 'turn-loop':
  137. results = PortRuntime().run_turn_loop(args.prompt, limit=args.limit, max_turns=args.max_turns, structured_output=args.structured_output)
  138. for idx, result in enumerate(results, start=1):
  139. print(f'## Turn {idx}')
  140. print(result.output)
  141. print(f'stop_reason={result.stop_reason}')
  142. return 0
  143. if args.command == 'flush-transcript':
  144. engine = QueryEnginePort.from_workspace()
  145. engine.submit_message(args.prompt)
  146. path = engine.persist_session()
  147. print(path)
  148. print(f'flushed={engine.transcript_store.flushed}')
  149. return 0
  150. if args.command == 'load-session':
  151. session = load_session(args.session_id)
  152. print(f'{session.session_id}\n{len(session.messages)} messages\nin={session.input_tokens} out={session.output_tokens}')
  153. return 0
  154. if args.command == 'remote-mode':
  155. print(run_remote_mode(args.target).as_text())
  156. return 0
  157. if args.command == 'ssh-mode':
  158. print(run_ssh_mode(args.target).as_text())
  159. return 0
  160. if args.command == 'teleport-mode':
  161. print(run_teleport_mode(args.target).as_text())
  162. return 0
  163. if args.command == 'direct-connect-mode':
  164. print(run_direct_connect(args.target).as_text())
  165. return 0
  166. if args.command == 'deep-link-mode':
  167. print(run_deep_link(args.target).as_text())
  168. return 0
  169. if args.command == 'show-command':
  170. module = get_command(args.name)
  171. if module is None:
  172. print(f'Command not found: {args.name}')
  173. return 1
  174. print('\n'.join([module.name, module.source_hint, module.responsibility]))
  175. return 0
  176. if args.command == 'show-tool':
  177. module = get_tool(args.name)
  178. if module is None:
  179. print(f'Tool not found: {args.name}')
  180. return 1
  181. print('\n'.join([module.name, module.source_hint, module.responsibility]))
  182. return 0
  183. if args.command == 'exec-command':
  184. result = execute_command(args.name, args.prompt)
  185. print(result.message)
  186. return 0 if result.handled else 1
  187. if args.command == 'exec-tool':
  188. result = execute_tool(args.name, args.payload)
  189. print(result.message)
  190. return 0 if result.handled else 1
  191. parser.error(f'unknown command: {args.command}')
  192. return 2
  193. if __name__ == '__main__':
  194. raise SystemExit(main())