hire-ai 0.1.1__py3-none-any.whl → 0.1.3__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.
- hire/__init__.py +1 -1
- hire/cli.py +20 -2
- hire/clipboard.py +40 -0
- hire/commands/__init__.py +2 -1
- hire/commands/ask.py +23 -2
- hire/commands/doctor.py +60 -0
- {hire_ai-0.1.1.dist-info → hire_ai-0.1.3.dist-info}/METADATA +9 -1
- {hire_ai-0.1.1.dist-info → hire_ai-0.1.3.dist-info}/RECORD +11 -9
- {hire_ai-0.1.1.dist-info → hire_ai-0.1.3.dist-info}/WHEEL +0 -0
- {hire_ai-0.1.1.dist-info → hire_ai-0.1.3.dist-info}/entry_points.txt +0 -0
- {hire_ai-0.1.1.dist-info → hire_ai-0.1.3.dist-info}/licenses/LICENSE +0 -0
hire/__init__.py
CHANGED
hire/cli.py
CHANGED
|
@@ -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"
|
hire/clipboard.py
ADDED
|
@@ -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
|
hire/commands/__init__.py
CHANGED
|
@@ -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"]
|
hire/commands/ask.py
CHANGED
|
@@ -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
|
-
|
|
164
|
+
output_text = json.dumps(output, indent=2, ensure_ascii=False)
|
|
162
165
|
else:
|
|
163
|
-
|
|
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
|
hire/commands/doctor.py
ADDED
|
@@ -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
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hire-ai
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.3
|
|
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
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
hire/__init__.py,sha256
|
|
2
|
-
hire/cli.py,sha256=
|
|
1
|
+
hire/__init__.py,sha256=YhA5hOy5-8RWTk6eNgont9ghfOS1X-7mfInyiDW0aho,90
|
|
2
|
+
hire/cli.py,sha256=qpjV_vUdd_p7q9BYwRIlCOpM3F2pwT_G4hsszA8IE-Y,4811
|
|
3
|
+
hire/clipboard.py,sha256=rDhB2lYNT3pPeHmTHXVMs6R_yLy1xm9ErInxjBM9MHo,1266
|
|
3
4
|
hire/config.py,sha256=s058dSnojmIdT1KRULWXchdMv6WM9aZ8CKgCqMGdVvA,1164
|
|
4
5
|
hire/paths.py,sha256=PMLwEil1qRmW-9fJ-nTnvWZOUuJvMc-N54xrPp2NHfA,1372
|
|
5
6
|
hire/session.py,sha256=Y-A2zCqiVshEOkBTNTAgaYu_FtCy1JBp4R91NMs5ffY,4874
|
|
@@ -8,13 +9,14 @@ hire/adapters/base.py,sha256=rubjoIXu7S8nIhBhksejR_Pb5YG4kwTZUj3dAMff_l4,1105
|
|
|
8
9
|
hire/adapters/claude.py,sha256=tM7G25jGqinjK_7QjtF_N0E089V9p1dzNReXJnq0VO4,1929
|
|
9
10
|
hire/adapters/codex.py,sha256=_4MXGTz-0YujuutDVCliaj9P94bm-Z1yNEElRtN3wzA,2895
|
|
10
11
|
hire/adapters/gemini.py,sha256=Qm_e1zzhxu2ooXANfFvVCwzxteVatlbDnBZeuf-jHNw,2613
|
|
11
|
-
hire/commands/__init__.py,sha256=
|
|
12
|
-
hire/commands/ask.py,sha256=
|
|
12
|
+
hire/commands/__init__.py,sha256=aX9gG9U6_ygeSCy577EoX31SzY-VL9Db1DRl7hT_bGg,249
|
|
13
|
+
hire/commands/ask.py,sha256=EPLigSMQlKmiEfMc8elRqelaC1urpNCr9YjttHbeorM,5876
|
|
13
14
|
hire/commands/delete.py,sha256=1fLZ64xmyIzR5s7i7lsNe-WornsD_qsOAe1cCdOodDU,991
|
|
15
|
+
hire/commands/doctor.py,sha256=QCPc1Eb_S8cNfs_xn0bw6UKj1OJTna_h1qTkueCN1Ms,1504
|
|
14
16
|
hire/commands/sessions.py,sha256=aIsh8oC4N3KtMeUdIrCvU3hONMxAfV_Z4qnprBiP62k,1073
|
|
15
17
|
hire/commands/show.py,sha256=gztFedJ553fvru0dPbHyperOMXDew-Yri3E3APu9d3c,860
|
|
16
|
-
hire_ai-0.1.
|
|
17
|
-
hire_ai-0.1.
|
|
18
|
-
hire_ai-0.1.
|
|
19
|
-
hire_ai-0.1.
|
|
20
|
-
hire_ai-0.1.
|
|
18
|
+
hire_ai-0.1.3.dist-info/METADATA,sha256=qN1iiAvZUJxHrEKrP0RbTUjk3A659uP9LgTlrnWRaDc,4032
|
|
19
|
+
hire_ai-0.1.3.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
20
|
+
hire_ai-0.1.3.dist-info/entry_points.txt,sha256=oq7wq8Ju9C2Lw79ljxSkQIaeKOHA_aIN9JKQAaDbz_Q,39
|
|
21
|
+
hire_ai-0.1.3.dist-info/licenses/LICENSE,sha256=Hj8NYmaOGk4xCcCYqD_5U91CVJwcJXEFSMWS965dUI0,1064
|
|
22
|
+
hire_ai-0.1.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|