message
This commit is contained in:
26
README.md
26
README.md
@@ -66,6 +66,32 @@ finish usage # tokens & cost
|
||||
Bash ≥4 or Zsh ≥5, curl, jq, bc.
|
||||
Optional: bash-completion.
|
||||
|
||||
```json
|
||||
{
|
||||
"provider": "lmstudio",
|
||||
"model": "darkidol-llama-3.1-8b-instruct-1.3-uncensored_gguf:2",
|
||||
"endpoint": "http://plato:1234/v1/chat/completions",
|
||||
"temperature": 0.0,
|
||||
"api_prompt_cost": 0.0,
|
||||
"api_completion_cost": 0.0,
|
||||
"max_history_commands": 20,
|
||||
"max_recent_files": 20,
|
||||
"cache_size": 100
|
||||
}
|
||||
```
|
||||
```json
|
||||
{
|
||||
"provider": "ollama",
|
||||
"model": "llama3:latest",
|
||||
"endpoint": "http://localhost:11434/api/chat",
|
||||
"temperature": 0.2,
|
||||
"api_prompt_cost": 0.0,
|
||||
"api_completion_cost": 0.0,
|
||||
"max_history_commands": 20,
|
||||
"max_recent_files": 20,
|
||||
"cache_size": 100
|
||||
}
|
||||
```
|
||||
## License
|
||||
|
||||
BSD 2-Clause.
|
||||
|
||||
8
queen.txt
Normal file
8
queen.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
|
||||
* ________/
|
||||
* / \
|
||||
* / \
|
||||
* | |
|
||||
* | |
|
||||
* Queen of Hearts
|
||||
|
||||
122
src/finish.py
122
src/finish.py
@@ -73,37 +73,91 @@ def _recent_files() -> str:
|
||||
except Exception:
|
||||
return ""
|
||||
|
||||
def _help_for(cmd: str) -> str:
|
||||
try:
|
||||
return subprocess.check_output([cmd, "--help"], stderr=subprocess.STDOUT, timeout=2).decode()
|
||||
except Exception:
|
||||
return f"{cmd} --help not available"
|
||||
def _installed_tools() -> str:
|
||||
"""Check for commonly used tools that might be relevant"""
|
||||
tools = ["curl", "wget", "jq", "grep", "awk", "sed", "python3", "node", "docker", "git", "nvcc", "nvidia-smi"]
|
||||
available = []
|
||||
for tool in tools:
|
||||
try:
|
||||
subprocess.run(["which", tool], capture_output=True, timeout=0.5, check=True)
|
||||
available.append(tool)
|
||||
except Exception:
|
||||
pass
|
||||
return ", ".join(available) if available else "standard shell tools"
|
||||
|
||||
def _get_context_info() -> dict:
|
||||
"""Gather comprehensive shell context"""
|
||||
return {
|
||||
"user": os.getenv("USER", "unknown"),
|
||||
"pwd": os.getcwd(),
|
||||
"home": os.getenv("HOME", ""),
|
||||
"hostname": os.getenv("HOSTNAME", os.getenv("HOST", "localhost")),
|
||||
"shell": os.getenv("SHELL", "bash"),
|
||||
"tools": _installed_tools(),
|
||||
}
|
||||
|
||||
def build_prompt(user_input: str) -> str:
|
||||
"""Build an enhanced prompt with better context and instructions"""
|
||||
c = cfg()
|
||||
term_info = f"""User: {os.getenv("USER")}
|
||||
PWD: {os.getcwd()}
|
||||
HOME: {os.getenv("HOME")}
|
||||
HOST: {os.getenv("HOSTNAME")}
|
||||
SHELL: bash"""
|
||||
prompt = f"""You are a helpful bash-completion script.
|
||||
Generate 2–5 concise, valid bash commands that complete the user’s intent.
|
||||
ctx = _get_context_info()
|
||||
|
||||
Reply **only** JSON: {{"completions":["cmd1","cmd2",...]}}
|
||||
# Analyze user intent
|
||||
user_words = user_input.lower().split()
|
||||
intent_hints = []
|
||||
if any(word in user_words for word in ["resolve", "get", "fetch", "download", "curl", "wget"]):
|
||||
intent_hints.append("The user wants to fetch/download data")
|
||||
if any(word in user_words for word in ["website", "url", "http", "html", "title"]):
|
||||
intent_hints.append("The user is working with web content")
|
||||
if any(word in user_words for word in ["parse", "extract", "grep", "find"]):
|
||||
intent_hints.append("The user wants to extract/parse data")
|
||||
if any(word in user_words for word in ["create", "make", "new", "write", "edit"]):
|
||||
intent_hints.append("The user wants to create or edit files")
|
||||
if any(word in user_words for word in ["file", "document", "text", "story", "about"]):
|
||||
intent_hints.append("The user is working with files/documents")
|
||||
if any(word in user_words for word in ["install", "setup", "configure", "apt", "pip"]):
|
||||
intent_hints.append("The user wants to install or configure software")
|
||||
if any(word in user_words for word in ["gpu", "nvidia", "cuda", "memory", "cpu"]):
|
||||
intent_hints.append("The user wants system/hardware information")
|
||||
|
||||
User command: {user_input}
|
||||
intent_context = "\n".join(f"- {hint}" for hint in intent_hints) if intent_hints else "- General command completion"
|
||||
|
||||
Terminal context:
|
||||
{term_info}
|
||||
prompt = f"""You are an intelligent bash completion assistant. Analyze the user's intent and provide practical, executable commands.
|
||||
|
||||
History:
|
||||
USER INPUT: {user_input}
|
||||
|
||||
DETECTED INTENT:
|
||||
{intent_context}
|
||||
|
||||
SHELL CONTEXT:
|
||||
- User: {ctx['user']}
|
||||
- Working directory: {ctx['pwd']}
|
||||
- Hostname: {ctx['hostname']}
|
||||
- Available tools: {ctx['tools']}
|
||||
|
||||
RECENT COMMAND HISTORY:
|
||||
{_sanitise_history()}
|
||||
|
||||
Recent files:
|
||||
{_recent_files()}
|
||||
INSTRUCTIONS:
|
||||
1. Understand what the user wants to accomplish (not just the literal words)
|
||||
2. Generate 2-5 practical bash commands that achieve the user's goal
|
||||
3. Prefer common tools (curl, wget, grep, awk, sed, jq, python)
|
||||
4. For file creation: use echo with heredoc, cat, or nano/vim
|
||||
5. For web tasks: use curl with headers, pipe to grep/sed/awk for parsing
|
||||
6. For complex parsing: suggest one-liners with proper escaping
|
||||
7. Commands should be copy-paste ready and executable
|
||||
8. Order by most likely to least likely intent
|
||||
|
||||
EXAMPLES:
|
||||
- "resolve title of website example.com" → curl -s https://example.com | grep -oP '<title>\\K[^<]+'
|
||||
- "show gpu status" → nvidia-smi
|
||||
- "download json from api.com" → curl -s https://api.com/data | jq .
|
||||
- "make a new file story.txt" → nano story.txt
|
||||
- "create file and write hello" → echo "hello" > file.txt
|
||||
- "write about dogs to file" → cat > dogs.txt << 'EOF' (then Ctrl+D to finish)
|
||||
|
||||
OUTPUT FORMAT (JSON only, no other text):
|
||||
{{"completions":["command1", "command2", "command3"]}}"""
|
||||
|
||||
Help:
|
||||
{_help_for(user_input.split()[0])}"""
|
||||
return prompt
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
@@ -155,9 +209,9 @@ async def llm_complete(prompt: str) -> List[str]:
|
||||
f.write(f"\n=== {time.strftime('%Y-%m-%d %H:%M:%S')} ===\n")
|
||||
f.write(f"Raw response:\n{raw}\n")
|
||||
|
||||
# try json first
|
||||
# try json first - use strict=False to be more lenient
|
||||
try:
|
||||
result = json.loads(raw)["completions"]
|
||||
result = json.loads(raw, strict=False)["completions"]
|
||||
if os.getenv("FINISH_DEBUG"):
|
||||
with open(LOG_FILE, "a") as f:
|
||||
f.write(f"Parsed completions: {result}\n")
|
||||
@@ -166,8 +220,28 @@ async def llm_complete(prompt: str) -> List[str]:
|
||||
if os.getenv("FINISH_DEBUG"):
|
||||
with open(LOG_FILE, "a") as f:
|
||||
f.write(f"JSON parse failed: {e}\n")
|
||||
f.write(f"Attempting manual extraction...\n")
|
||||
|
||||
# Try to extract JSON array manually using regex
|
||||
try:
|
||||
match = re.search(r'"completions"\s*:\s*\[(.*?)\]', raw, re.DOTALL)
|
||||
if match:
|
||||
# Extract commands from the array - handle escaped quotes
|
||||
array_content = match.group(1)
|
||||
# Find all quoted strings
|
||||
commands = re.findall(r'"([^"\\]*(?:\\.[^"\\]*)*)"', array_content)
|
||||
if commands:
|
||||
if os.getenv("FINISH_DEBUG"):
|
||||
with open(LOG_FILE, "a") as f:
|
||||
f.write(f"Manual extraction succeeded: {commands}\n")
|
||||
return commands[:5]
|
||||
except Exception as e2:
|
||||
if os.getenv("FINISH_DEBUG"):
|
||||
with open(LOG_FILE, "a") as f:
|
||||
f.write(f"Manual extraction failed: {e2}\n")
|
||||
|
||||
# fallback: grep command-like lines
|
||||
fallback = [ln for ln in raw.splitlines() if re.match(r"^(ls|cd|find|cat|grep|echo|mkdir|rm|cp|mv|pwd|chmod|chown)\b", ln)][:5]
|
||||
fallback = [ln for ln in raw.splitlines() if re.match(r"^(ls|cd|find|cat|grep|echo|mkdir|rm|cp|mv|pwd|chmod|chown|nano|vim|touch)\b", ln)][:5]
|
||||
if os.getenv("FINISH_DEBUG"):
|
||||
with open(LOG_FILE, "a") as f:
|
||||
f.write(f"Fallback completions: {fallback}\n")
|
||||
|
||||
Reference in New Issue
Block a user