hire-ai 0.1.1__tar.gz → 0.1.2__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hire-ai
3
- Version: 0.1.1
3
+ Version: 0.1.2
4
4
  Summary: CLI to orchestrate AI agents (Claude, Codex, Gemini)
5
5
  Project-URL: Homepage, https://github.com/nichiki/hire-ai
6
6
  Project-URL: Repository, https://github.com/nichiki/hire-ai
@@ -91,6 +91,12 @@ hire codex "Explain @package.json and @tsconfig.json"
91
91
  # Output as JSON
92
92
  hire gemini "Summarize this" --json
93
93
 
94
+ # Copy to clipboard
95
+ hire codex "Write a function" --clip
96
+
97
+ # Write to file
98
+ hire claude "Generate a README" -o README.md
99
+
94
100
  # Session management
95
101
  hire sessions # List all sessions
96
102
  hire sessions codex # List Codex sessions only
@@ -107,6 +113,8 @@ hire delete SESSION_ID # Delete a session
107
113
  | `-n, --name NAME` | Name the session |
108
114
  | `-m, --model MODEL` | Specify model to use |
109
115
  | `--json` | Output in JSON format |
116
+ | `--clip` | Copy output to clipboard |
117
+ | `-o, --out FILE` | Write output to file |
110
118
 
111
119
  ## Configuration
112
120
 
@@ -59,6 +59,12 @@ hire codex "Explain @package.json and @tsconfig.json"
59
59
  # Output as JSON
60
60
  hire gemini "Summarize this" --json
61
61
 
62
+ # Copy to clipboard
63
+ hire codex "Write a function" --clip
64
+
65
+ # Write to file
66
+ hire claude "Generate a README" -o README.md
67
+
62
68
  # Session management
63
69
  hire sessions # List all sessions
64
70
  hire sessions codex # List Codex sessions only
@@ -75,6 +81,8 @@ hire delete SESSION_ID # Delete a session
75
81
  | `-n, --name NAME` | Name the session |
76
82
  | `-m, --model MODEL` | Specify model to use |
77
83
  | `--json` | Output in JSON format |
84
+ | `--clip` | Copy output to clipboard |
85
+ | `-o, --out FILE` | Write output to file |
78
86
 
79
87
  ## Configuration
80
88
 
@@ -4,10 +4,10 @@ import argparse
4
4
  import sys
5
5
 
6
6
  from . import __version__
7
- from .commands import run_ask, run_delete, run_sessions, run_show
7
+ from .commands import run_ask, run_delete, run_doctor, run_sessions, run_show
8
8
 
9
9
 
10
- SUBCOMMANDS = {"sessions", "show", "delete", "help", "--help", "-h", "--version"}
10
+ SUBCOMMANDS = {"sessions", "show", "delete", "doctor", "help", "--help", "-h", "--version"}
11
11
 
12
12
 
13
13
  def main() -> int:
@@ -68,6 +68,9 @@ def main() -> int:
68
68
  help="Delete without confirmation",
69
69
  )
70
70
 
71
+ # doctor command
72
+ subparsers.add_parser("doctor", help="Check environment and agent availability")
73
+
71
74
  args = parser.parse_args()
72
75
 
73
76
  if args.command is None:
@@ -81,6 +84,8 @@ def main() -> int:
81
84
  return run_show(args)
82
85
  elif args.command == "delete":
83
86
  return run_delete(args)
87
+ elif args.command == "doctor":
88
+ return run_doctor(args)
84
89
  else:
85
90
  print_usage()
86
91
  return 1
@@ -126,6 +131,16 @@ def run_default() -> int:
126
131
  action="store_true",
127
132
  help="Output in JSON format",
128
133
  )
134
+ parser.add_argument(
135
+ "--clip",
136
+ action="store_true",
137
+ help="Copy output to clipboard",
138
+ )
139
+ parser.add_argument(
140
+ "-o", "--out",
141
+ metavar="FILE",
142
+ help="Write output to file",
143
+ )
129
144
 
130
145
  args = parser.parse_args()
131
146
  return run_ask(args)
@@ -141,6 +156,7 @@ Usage:
141
156
  hire sessions [target] List sessions
142
157
  hire show <name-or-id> Show session details
143
158
  hire delete <name-or-id> Delete a session
159
+ hire doctor Check environment
144
160
 
145
161
  Targets:
146
162
  claude, codex, gemini
@@ -151,6 +167,8 @@ Options:
151
167
  -n, --name NAME Name the session
152
168
  -m, --model MODEL Specify model
153
169
  --json Output in JSON format
170
+ --clip Copy output to clipboard
171
+ -o, --out FILE Write output to file
154
172
 
155
173
  Examples:
156
174
  hire codex "Design a REST API"
@@ -0,0 +1,40 @@
1
+ """Clipboard utilities."""
2
+
3
+ import platform
4
+ import subprocess
5
+
6
+
7
+ def copy_to_clipboard(text: str) -> bool:
8
+ """Copy text to system clipboard. Returns True on success."""
9
+ system = platform.system()
10
+ try:
11
+ if system == "Darwin": # macOS
12
+ # Don't override env - let system locale handle it
13
+ subprocess.run(
14
+ ["pbcopy"],
15
+ input=text,
16
+ text=True,
17
+ encoding="utf-8",
18
+ check=True,
19
+ )
20
+ elif system == "Linux":
21
+ # Try xclip first, fall back to xsel
22
+ try:
23
+ subprocess.run(
24
+ ["xclip", "-selection", "clipboard"],
25
+ input=text.encode("utf-8"),
26
+ check=True,
27
+ )
28
+ except FileNotFoundError:
29
+ subprocess.run(
30
+ ["xsel", "--clipboard", "--input"],
31
+ input=text.encode("utf-8"),
32
+ check=True,
33
+ )
34
+ elif system == "Windows":
35
+ subprocess.run(["clip"], input=text.encode("utf-8"), check=True)
36
+ else:
37
+ return False
38
+ return True
39
+ except (subprocess.CalledProcessError, FileNotFoundError):
40
+ return False
@@ -4,5 +4,6 @@ from .ask import run_ask
4
4
  from .sessions import run_sessions
5
5
  from .show import run_show
6
6
  from .delete import run_delete
7
+ from .doctor import run_doctor
7
8
 
8
- __all__ = ["run_ask", "run_sessions", "run_show", "run_delete"]
9
+ __all__ = ["run_ask", "run_sessions", "run_show", "run_delete", "run_doctor"]
@@ -5,6 +5,7 @@ import sys
5
5
  from argparse import Namespace
6
6
 
7
7
  from ..adapters import get_adapter
8
+ from ..clipboard import copy_to_clipboard
8
9
  from ..session import (
9
10
  create_session,
10
11
  find_session,
@@ -45,6 +46,8 @@ def run_ask(args: Namespace) -> int:
45
46
  name = args.name
46
47
  model = args.model
47
48
  output_json = args.json
49
+ copy_clip = getattr(args, "clip", False)
50
+ out_file = getattr(args, "out", None)
48
51
 
49
52
  # Handle case where target is actually the message (when target is omitted)
50
53
  # e.g., "hire 'message'" -> target='message', message=None
@@ -158,8 +161,26 @@ def run_ask(args: Namespace) -> int:
158
161
  "agent": target,
159
162
  "name": session.get("name"),
160
163
  }
161
- print(json.dumps(output, indent=2, ensure_ascii=False))
164
+ output_text = json.dumps(output, indent=2, ensure_ascii=False)
162
165
  else:
163
- print(result.get("response", ""))
166
+ output_text = result.get("response", "")
167
+
168
+ print(output_text)
169
+
170
+ # Copy to clipboard if requested
171
+ if copy_clip:
172
+ if copy_to_clipboard(output_text):
173
+ print("\n(Copied to clipboard)", file=sys.stderr)
174
+ else:
175
+ print("\n(Failed to copy to clipboard)", file=sys.stderr)
176
+
177
+ # Write to file if requested
178
+ if out_file:
179
+ try:
180
+ with open(out_file, "w", encoding="utf-8") as f:
181
+ f.write(output_text)
182
+ print(f"\n(Written to {out_file})", file=sys.stderr)
183
+ except OSError as e:
184
+ print(f"\n(Failed to write to {out_file}: {e})", file=sys.stderr)
164
185
 
165
186
  return 0
@@ -0,0 +1,60 @@
1
+ """Doctor command implementation."""
2
+
3
+ import shutil
4
+ import sys
5
+ from argparse import Namespace
6
+
7
+ from .. import __version__
8
+ from ..paths import get_config_path, get_sessions_dir
9
+
10
+
11
+ AGENTS = {
12
+ "claude": "claude",
13
+ "codex": "codex",
14
+ "gemini": "gemini",
15
+ }
16
+
17
+
18
+ def run_doctor(args: Namespace) -> int:
19
+ """Run the doctor command to check environment."""
20
+ print(f"hire-ai v{__version__}")
21
+ print(f"Python {sys.version.split()[0]}")
22
+ print()
23
+
24
+ # Check agents
25
+ print("Checking agents...")
26
+ found = 0
27
+ missing = 0
28
+ for name, cmd in AGENTS.items():
29
+ path = shutil.which(cmd)
30
+ if path:
31
+ print(f" \u2713 {name} - {path}")
32
+ found += 1
33
+ else:
34
+ print(f" \u2717 {name} - not found")
35
+ missing += 1
36
+ print()
37
+
38
+ # Check config
39
+ print("Checking config...")
40
+ config_path = get_config_path()
41
+ if config_path.exists():
42
+ print(f" \u2713 Config: {config_path}")
43
+ else:
44
+ print(f" - Config: {config_path} (not created yet, using defaults)")
45
+
46
+ sessions_dir = get_sessions_dir()
47
+ session_count = sum(1 for _ in sessions_dir.rglob("*.json"))
48
+ print(f" \u2713 Sessions: {sessions_dir} ({session_count} sessions)")
49
+ print()
50
+
51
+ # Summary
52
+ if missing == 0:
53
+ print("All good!")
54
+ elif found == 0:
55
+ print("No agents found. Install at least one of: claude, codex, gemini")
56
+ return 1
57
+ else:
58
+ print(f"Ready! ({missing} agent(s) not installed)")
59
+
60
+ return 0
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "hire-ai"
7
- version = "0.1.1"
7
+ version = "0.1.2"
8
8
  description = "CLI to orchestrate AI agents (Claude, Codex, Gemini)"
9
9
  readme = "README.md"
10
10
  license = "MIT"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes