Compare commits
16 Commits
graph-agents
...
v0.5.0
| Author | SHA1 | Date | |
|---|---|---|---|
| b1cd8351fa | |||
| ccf5e73341 | |||
| be5d280c32 | |||
| 6633a8c0bf | |||
| 097d8936e3 | |||
| 8a53b7934b | |||
| 0facb15e32 | |||
| c172736362 | |||
| 4a2b9fa42a | |||
| 98db37866c | |||
| ad31fbd169 | |||
| d69e28fd39 | |||
| 279eaa5300 | |||
| e687d78931 | |||
| 0c2e4df647 | |||
| 6221875f64 |
@@ -21,25 +21,25 @@ body:
|
||||
value: |
|
||||
I tried this:
|
||||
|
||||
1. `loki`
|
||||
1. `coyote`
|
||||
|
||||
I expected this to happen:
|
||||
|
||||
Instead, this happened:
|
||||
- type: textarea
|
||||
id: loki-log
|
||||
id: coyote-log
|
||||
attributes:
|
||||
label: Loki log
|
||||
description: Include the Loki log file to help diagnose the issue. (`loki --info` to see the log_path)
|
||||
label: Coyote log
|
||||
description: Include the Coyote log file to help diagnose the issue. (`coyote --info` to see the log_path)
|
||||
value: |
|
||||
| OS | Log file location |
|
||||
| ------- | ----------------------------------------------------- |
|
||||
| Linux | `~/.cache/loki/loki.log` |
|
||||
| Mac | `~/Library/Logs/loki/loki.log` |
|
||||
| Windows | `C:\Users\<User>\AppData\Local\loki\loki.log` |
|
||||
| Linux | `~/.cache/coyote/coyote.log` |
|
||||
| Mac | `~/Library/Logs/coyote/coyote.log` |
|
||||
| Windows | `C:\Users\<User>\AppData\Local\coyote\coyote.log` |
|
||||
|
||||
```
|
||||
please provide a copy of your loki log file here if possible; you may need to redact some of the lines
|
||||
please provide a copy of your coyote log file here if possible; you may need to redact some of the lines
|
||||
```
|
||||
|
||||
- type: input
|
||||
@@ -57,13 +57,13 @@ body:
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: loki-version
|
||||
id: coyote-version
|
||||
attributes:
|
||||
label: Loki Version
|
||||
label: Coyote Version
|
||||
description: >
|
||||
Loki version (`loki --version` if using a release, `git describe` if building
|
||||
Coyote version (`coyote --version` if using a release, `git describe` if building
|
||||
from main).
|
||||
**Make sure that you are using the [latest loki release](https://github.com/Dark-Alex-17/loki/releases) or a newer main build**
|
||||
placeholder: "loki 0.1.0"
|
||||
**Make sure that you are using the [latest coyote release](https://github.com/Dark-Alex-17/coyote/releases) or a newer main build**
|
||||
placeholder: "coyote 0.1.0"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
@@ -98,9 +98,9 @@ jobs:
|
||||
# Ignore Act's local artifact dir noise
|
||||
echo artifacts/ >> .git/info/exclude || true
|
||||
|
||||
# Edit the version line right after name="loki"
|
||||
# Edit the version line right after name="coyote"
|
||||
sed -E -i '
|
||||
/^[[:space:]]*name[[:space:]]*=[[:space:]]*"loki"[[:space:]]*$/ {
|
||||
/^[[:space:]]*name[[:space:]]*=[[:space:]]*"coyote"[[:space:]]*$/ {
|
||||
n
|
||||
s|^[[:space:]]*version[[:space:]]*=[[:space:]]*"[^"]*"|version = "'"$VERSION"'"|
|
||||
}
|
||||
@@ -278,7 +278,7 @@ jobs:
|
||||
- name: Verify file
|
||||
shell: bash
|
||||
run: |
|
||||
file target/${{ matrix.target }}/release/loki
|
||||
file target/${{ matrix.target }}/release/coyote
|
||||
|
||||
- name: Test
|
||||
if: matrix.target != 'aarch64-apple-darwin' && matrix.target != 'aarch64-pc-windows-msvc'
|
||||
@@ -382,11 +382,11 @@ jobs:
|
||||
shell: bash
|
||||
run: |
|
||||
# Set environment variables
|
||||
macos_sha="$(cat ./artifacts/loki-x86_64-apple-darwin.sha256 | awk '{print $1}')"
|
||||
macos_sha="$(cat ./artifacts/coyote-x86_64-apple-darwin.sha256 | awk '{print $1}')"
|
||||
echo "MACOS_SHA=$macos_sha" >> $GITHUB_ENV
|
||||
macos_sha_arm="$(cat ./artifacts/loki-aarch64-apple-darwin.sha256 | awk '{print $1}')"
|
||||
macos_sha_arm="$(cat ./artifacts/coyote-aarch64-apple-darwin.sha256 | awk '{print $1}')"
|
||||
echo "MACOS_SHA_ARM=$macos_sha_arm" >> $GITHUB_ENV
|
||||
linux_sha="$(cat ./artifacts/loki-x86_64-unknown-linux-musl.sha256 | awk '{print $1}')"
|
||||
linux_sha="$(cat ./artifacts/coyote-x86_64-unknown-linux-musl.sha256 | awk '{print $1}')"
|
||||
echo "LINUX_SHA=$linux_sha" >> $GITHUB_ENV
|
||||
release_version="$(cat ./artifacts/release-version)"
|
||||
echo "RELEASE_VERSION=$release_version" >> $GITHUB_ENV
|
||||
@@ -402,23 +402,23 @@ jobs:
|
||||
if: env.ACT != 'true'
|
||||
run: |
|
||||
# run packaging script
|
||||
python "./deployment/homebrew/packager.py" ${{ env.RELEASE_VERSION }} "./deployment/homebrew/loki.rb.template" "./loki.rb" ${{ env.MACOS_SHA }} ${{ env.MACOS_SHA_ARM }} ${{ env.LINUX_SHA }}
|
||||
python "./deployment/homebrew/packager.py" ${{ env.RELEASE_VERSION }} "./deployment/homebrew/coyote.rb.template" "./coyote.rb" ${{ env.MACOS_SHA }} ${{ env.MACOS_SHA_ARM }} ${{ env.LINUX_SHA }}
|
||||
|
||||
- name: Push changes to Homebrew tap
|
||||
if: env.ACT != 'true'
|
||||
env:
|
||||
TOKEN: ${{ secrets.LOKI_GITHUB_TOKEN }}
|
||||
TOKEN: ${{ secrets.COYOTE_GITHUB_TOKEN }}
|
||||
run: |
|
||||
# push to Git
|
||||
git config --global user.name "Dark-Alex-17"
|
||||
git config --global user.email "alex.j.tusa@gmail.com"
|
||||
git clone https://Dark-Alex-17:${{ secrets.LOKI_GITHUB_TOKEN }}@github.com/Dark-Alex-17/homebrew-loki.git
|
||||
rm homebrew-loki/Formula/loki.rb
|
||||
cp loki.rb homebrew-loki/Formula
|
||||
cd homebrew-loki
|
||||
git clone https://Dark-Alex-17:${{ secrets.COYOTE_GITHUB_TOKEN }}@github.com/Dark-Alex-17/homebrew-coyote.git
|
||||
rm homebrew-coyote/Formula/coyote.rb
|
||||
cp coyote.rb homebrew-coyote/Formula
|
||||
cd homebrew-coyote
|
||||
git add .
|
||||
git diff-index --quiet HEAD || git commit -am "Update formula for Loki release ${{ env.RELEASE_VERSION }}"
|
||||
git push https://$TOKEN@github.com/Dark-Alex-17/homebrew-loki.git
|
||||
git diff-index --quiet HEAD || git commit -am "Update formula for Coyote release ${{ env.RELEASE_VERSION }}"
|
||||
git push https://$TOKEN@github.com/Dark-Alex-17/homebrew-coyote.git
|
||||
|
||||
publish-crate:
|
||||
needs: publish-github-release
|
||||
|
||||
+1
-1
@@ -3,5 +3,5 @@
|
||||
/.env
|
||||
!cli/**
|
||||
.idea/
|
||||
/loki.iml
|
||||
/coyote.iml
|
||||
/.idea/
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
{"type":"rust","build":"cargo build","test":"cargo test","check":"cargo check","_detected_by":"heuristic","_cached_at":"2026-04-13T13:36:33-06:00"}
|
||||
+115
-4
@@ -1,3 +1,114 @@
|
||||
## v0.5.0 (2026-05-27)
|
||||
|
||||
### Feat
|
||||
|
||||
- rename Loki to Coyote
|
||||
|
||||
### Fix
|
||||
|
||||
- bash-based user interactions in agents accidentally regressed in graph implementation
|
||||
- Claude function calling in agent contexts
|
||||
- Claude code rate limit error per new Claude changes
|
||||
|
||||
## v0.4.0 (2026-05-23)
|
||||
|
||||
### Feat
|
||||
|
||||
- LLM node failures propgate up
|
||||
- Added .install remote tab completions to the REPL
|
||||
- feature complete install remote with category selection
|
||||
- Support to interactively add secrets to Coyote that are missing from MCP configs when merging
|
||||
- Added MCP config merging support for remote asset installations
|
||||
- install remote now writes files to disk
|
||||
- Created basic install_remote functions
|
||||
- Created a more comprehensive and immediately useful default config for first runs
|
||||
- Created an example graph-based agent called deep-research
|
||||
- Improved coder agent that is now a graph-based agent
|
||||
- Removed indicatif spinners. The UX just won't stop clobbering for parallel graph nodes
|
||||
- Added agent variables support for graph agents and improved script executor to use the same environment variables as normal agent tool calling for further flexibility
|
||||
- Improved UX with colored spinners for parallel graph agents and no clobbering outputs for sub-agents
|
||||
- created new graph-based deep-research agent
|
||||
- improved UX for parallel graph execution
|
||||
- added branch progress tracker for better visualization of parallel graph super-steps
|
||||
- Removed the jira-helper agent and replaced it with the atlassian role
|
||||
- created the RenderMode enum to suppress stdout streaming during parallel graph super-steps
|
||||
- Full support for map node types
|
||||
- implemented the frontier-based scheduling for the graph executor with simplified state management (gotta love .clone)
|
||||
- validation support for parallel graph execution; restricted map nodes to only run for nodes without next targets and not supporting chained map nodes
|
||||
- created the staging area for state merges per super-step and created the built-in reducers (and their application) for the state merge phase of a super step
|
||||
- scaffolding work for fan-out nodes for parallel branch execution support and stubbed out Map node types
|
||||
- Coyote can now update itself via .update and --update commands
|
||||
- added a .edit command for editing the MCP configuration file
|
||||
- Created a new .install command to install bundled assets on-demand
|
||||
- migrated llm node validation to graph loading time instead of graph runtime
|
||||
- ripped out user input timeout scaffolding for approval and input node types; implementation can't be done cleanly
|
||||
- added additional support for all RAG-configuration fields in RAG nodes
|
||||
- initial support for RAG nodes in the graph execution system
|
||||
- implemented structured logging for graph execution
|
||||
- merged normal agent config and graph agent configs into one file (either/or)
|
||||
- added structured-output extraction for llm and agent nodes
|
||||
- created full llm node runtime implementation
|
||||
- scaffolded together the initial llm node type and its executor
|
||||
- wired together graph execution and agent graph dispatch
|
||||
- implemented support for the graph executor
|
||||
- created the approval node executor and the input node executor for user interaction
|
||||
- Added initial support for native Coyote agent nodes in the graph-based agent system
|
||||
- Added direct script invocation support for graph-based agents
|
||||
- Added graph validation
|
||||
- Implemented state management for agent graphs
|
||||
- initial agent graph scaffolding
|
||||
- add auto-continue support to all contexts
|
||||
- dynamic tab completions now show the sessions for a given agent instead of only listing global sessions
|
||||
- legacy SSE support for MCP server configurations
|
||||
- support http/sse transport types for MCP server configurations so it fully supports claude desktop-style MCP configs
|
||||
- 99% complete migration to new state structs to get away from God-Config struct; i.e. AppConfig, AppState, and RequestContext
|
||||
- Automatic runtime customization using shebangs
|
||||
- Created a demo TypeScript tool and a get_current_weather function in TypeScript
|
||||
- Updated the Python demo tool to show all possible parameter types and variations
|
||||
- Added TypeScript tool support using the refactored common ScriptedLanguage trait
|
||||
|
||||
### Fix
|
||||
|
||||
- Generified the functions usage of script detection for an executable bit on unix systems
|
||||
- merge required claude code system prompt into instructions
|
||||
- updated argc argument passing in run-tool and run-agent scripts
|
||||
- Added additional graph validation for parallel reads and writes with dependencies between nodes states
|
||||
- bug in next_single method and improved outcome handling for LLM node execution
|
||||
- inline RAG bug when globbing files by extension without subdirectory globbing
|
||||
- update the estimate_token_length function to use the standard word count method
|
||||
- removed unnecessary regenerate logic for sessions and use the same logic for all contexts; prevents a panic on empty message list
|
||||
- error when users try to start a session on a graph agent
|
||||
- added on_other field for approval nodes so users can specify an alternative free-text target when none of the options match what they want
|
||||
- accidentally added back in full agent tools on LLM nodes
|
||||
- Improve the coder agent's usage of tools
|
||||
- make the agent__collect escalation-aware so it doesn't freeze on sub-agent escalations
|
||||
- check for an existing session before starting up MCP servers when switching to a role
|
||||
- do not switch to agent if a session is active.
|
||||
- Do not append todo instructions when function calling is disabled
|
||||
- a bug in the dynamic completions because the crate name is coyote-ai but the binary is named coyote
|
||||
- bug found by copilot that would create a lock on the PollSender for sse-based MCP servers
|
||||
- Accidental shadow of temp_file function for Windows function calling
|
||||
- upgraded to newer rmcp version to get native-tls support
|
||||
- RagCache was not being used for agent and sub-agent instantiation
|
||||
- TypeScript function args were being passed as objects rather than direct parameters
|
||||
- Added in forgotten wrapper scripts for TypeScript tools
|
||||
- don't shadow variables in binary path handling for Windows
|
||||
- Tool call improvements for Windows systems
|
||||
|
||||
### Refactor
|
||||
|
||||
- migrated llm nodes to use Roles to simplify instructions handling and to function like inline roles
|
||||
- migrated the next_node and apply_state_updates logic for LLM nodes into the LlmExecutor
|
||||
- fully complete state re-architecting
|
||||
- Fully ripped out the god Config struct
|
||||
- Deprecated old Config struct initialization logic
|
||||
- migrate functions and MCP servers to AppConfig
|
||||
- Migrate the vault/bare_init logic
|
||||
- created a single install_builtins free function to remove from Config::init
|
||||
- partial migration to init in AppConfig
|
||||
- Extracted common Python parser logic into a common.rs module
|
||||
- python tools now use tree-sitter queries instead of AST
|
||||
|
||||
## v0.3.0 (2026-04-02)
|
||||
|
||||
### Feat
|
||||
@@ -21,7 +132,7 @@
|
||||
- Created a CodeRabbit-style code-reviewer agent
|
||||
- Added configuration option in agents to indicate the timeout for user input before proceeding (defaults to 5 minutes)
|
||||
- Added support for sub-agents to escalate user interaction requests from any depth to the parent agents for user interactions
|
||||
- built-in user interaction tools to remove the need for the list/confirm/etc prompts in prompt tools and to enhance user interactions in Loki
|
||||
- built-in user interaction tools to remove the need for the list/confirm/etc prompts in prompt tools and to enhance user interactions in Coyote
|
||||
- Experimental update to sisyphus to use the new parallel agent spawning system
|
||||
- Added an agent configuration property that allows auto-injecting sub-agent spawning instructions (when using the built-in sub-agent spawning system)
|
||||
- Auto-dispatch support of sub-agents and support for the teammate pattern between subagents
|
||||
@@ -75,7 +186,7 @@
|
||||
|
||||
- Simplified sisyphus prompt to improve functionality
|
||||
- Supported the injection of RAG sources into the prompt, not just via the `.sources rag` command in the REPL so models can directly reference the documents that supported their responses
|
||||
- Created the Sisyphus agent to make Loki function like Claude Code, Gemini, Codex, etc.
|
||||
- Created the Sisyphus agent to make Coyote function like Claude Code, Gemini, Codex, etc.
|
||||
- Created the Oracle agent to handle high-level architectural decisions and design questions about a given codebase
|
||||
- Updated the coder agent to be much more task-focused and to be delegated to by Sisyphus
|
||||
- Created the explore agent for exploring codebases to help answer questions
|
||||
@@ -135,8 +246,8 @@
|
||||
- Support for secret injection into the global config file (API keys, for example)
|
||||
- Improved MCP handling toggle handling
|
||||
- Secret injection into the MCP configuration
|
||||
- added REPL support for interacting with the Loki vault
|
||||
- Integrated gman with Loki to create a vault and added flags to configure the Loki vault
|
||||
- added REPL support for interacting with the Coyote vault
|
||||
- Integrated gman with Coyote to create a vault and added flags to configure the Coyote vault
|
||||
- Added a default session to the jira helper to make interaction more natural
|
||||
- Created the repo-analyzer role
|
||||
- Created the coder and sql agents
|
||||
|
||||
+2
-2
@@ -2,7 +2,7 @@
|
||||
Contributors are very welcome! **No contribution is too small and all contributions are valued.**
|
||||
|
||||
## Rust
|
||||
You'll need to have the stable Rust toolchain installed in order to develop Loki.
|
||||
You'll need to have the stable Rust toolchain installed in order to develop Coyote.
|
||||
|
||||
The Rust toolchain (stable) can be installed via rustup using the following command:
|
||||
|
||||
@@ -84,5 +84,5 @@ Claude, etc.) is not permitted unless explicitly disclosed and approved.
|
||||
Submissions must certify that the contributor understands and can maintain the code they submit.
|
||||
|
||||
## Questions? Reach out to me!
|
||||
If you encounter any questions while developing Loki, please don't hesitate to reach out to me at
|
||||
If you encounter any questions while developing Coyote, please don't hesitate to reach out to me at
|
||||
alex.j.tusa@gmail.com. I'm happy to help contributors in any way I can, regardless of if they're new or experienced!
|
||||
|
||||
+6
-6
@@ -1,19 +1,19 @@
|
||||
# Credits
|
||||
|
||||
## AIChat
|
||||
Loki originally started as a fork of the fantastic
|
||||
Coyote originally started as a fork of the fantastic
|
||||
[AIChat CLI](https://github.com/sigoden/aichat). The initial goal was simply
|
||||
to fix a bug in how MCP servers worked with AIChat, allowing different MCP
|
||||
servers to be specified per agent. Since then, Loki has evolved far beyond
|
||||
servers to be specified per agent. Since then, Coyote has evolved far beyond
|
||||
its original scope and grown into a passion project with a life of its own.
|
||||
|
||||
Today, Loki includes first-class MCP server support (for both local and remote
|
||||
Today, Coyote includes first-class MCP server support (for both local and remote
|
||||
servers), a built-in vault for interpolating secrets in configuration files,
|
||||
built-in agents and macros, dynamic tab completions, integrated custom
|
||||
functions (no external `argc` dependency), improved documentation, and much
|
||||
more with many more ideas planned for the future.
|
||||
|
||||
Loki is now developed and maintained as an independent project. Full credit
|
||||
Coyote is now developed and maintained as an independent project. Full credit
|
||||
for the original foundation goes to the developers of the wonderful
|
||||
AIChat project.
|
||||
|
||||
@@ -21,10 +21,10 @@ This project is not affiliated with or endorsed by the AIChat maintainers.
|
||||
|
||||
## AIChat
|
||||
|
||||
Loki originally began as a fork of [AIChat CLI](https://github.com/sigoden/aichat),
|
||||
Coyote originally began as a fork of [AIChat CLI](https://github.com/sigoden/aichat),
|
||||
created and maintained by the AIChat contributors.
|
||||
|
||||
While Loki has since diverged significantly and is now developed as an
|
||||
While Coyote has since diverged significantly and is now developed as an
|
||||
independent project, its early foundation and inspiration came from the
|
||||
AIChat project.
|
||||
|
||||
|
||||
Generated
+316
-196
File diff suppressed because it is too large
Load Diff
+6
-6
@@ -1,16 +1,16 @@
|
||||
[package]
|
||||
name = "loki-ai"
|
||||
version = "0.3.0"
|
||||
name = "coyote-ai"
|
||||
version = "0.5.0"
|
||||
edition = "2024"
|
||||
authors = ["Alex Clarke <alex.j.tusa@gmail.com>"]
|
||||
description = "An all-in-one, batteries included LLM CLI Tool"
|
||||
keywords = ["chatgpt", "llm", "cli", "ai", "repl"]
|
||||
homepage = "https://github.com/Dark-Alex-17/loki"
|
||||
repository = "https://github.com/Dark-Alex-17/loki"
|
||||
homepage = "https://github.com/Dark-Alex-17/coyote"
|
||||
repository = "https://github.com/Dark-Alex-17/coyote"
|
||||
categories = ["command-line-utilities"]
|
||||
readme = "README.md"
|
||||
license = "MIT"
|
||||
rust-version = "1.89.0"
|
||||
rust-version = "1.95.0"
|
||||
exclude = [".github", "CONTRIBUTING.md"]
|
||||
|
||||
[dependencies]
|
||||
@@ -138,7 +138,7 @@ pretty_assertions = "1.4.0"
|
||||
serial_test = "3"
|
||||
|
||||
[[bin]]
|
||||
name = "loki"
|
||||
name = "coyote"
|
||||
path = "src/main.rs"
|
||||
|
||||
[profile.release]
|
||||
|
||||
@@ -1,54 +1,54 @@
|
||||
# Loki: All-in-one, batteries-included LLM CLI Tool
|
||||
# Coyote: All-in-one, batteries-included LLM CLI Tool
|
||||
|
||||

|
||||
[](https://crates.io/crates/loki-ai)
|
||||

|
||||

|
||||
[](https://github.com/Dark-Alex-17/loki/releases)
|
||||

|
||||
[](https://crates.io/crates/coyote-ai)
|
||||

|
||||

|
||||
[](https://github.com/Dark-Alex-17/coyote/releases)
|
||||
|
||||
Loki is an all-in-one, batteries-included, LLM CLI tool featuring Shell Assistant, CLI & REPL Mode, RAG, AI Tools &
|
||||
Coyote is an all-in-one, batteries-included, LLM CLI tool featuring Shell Assistant, CLI & REPL Mode, RAG, AI Tools &
|
||||
Agents, and More.
|
||||
|
||||
It is designed to include a number of useful agents, roles, macros, and more so users can get up and running with Loki
|
||||
It is designed to include a number of useful agents, roles, macros, and more so users can get up and running with Coyote
|
||||
in as little time as possible. You can also install entire bundles of agents, roles, macros, tools, and MCP servers from
|
||||
any git repository — see [Sharing Configurations](#sharing-configurations).
|
||||
any git repository. See [Sharing Configurations](https://github.com/Dark-Alex-17/coyote/wiki/Sharing-Configurations) for more information.
|
||||
|
||||

|
||||

|
||||
|
||||
Coming from [AIChat](https://github.com/sigoden/aichat)? Follow the [migration guide](https://github.com/Dark-Alex-17/loki/wiki/AIChat-Migration) to get started.
|
||||
Coming from [AIChat](https://github.com/sigoden/aichat)? Follow the [migration guide](https://github.com/Dark-Alex-17/coyote/wiki/AIChat-Migration) to get started.
|
||||
|
||||
## Quick Links
|
||||
* [AIChat Migration Guide](https://github.com/Dark-Alex-17/loki/wiki/AIChat-Migration): Coming from AIChat? Follow the migration guide to get started.
|
||||
* [Installation](#install): Install Loki
|
||||
* [Getting Started](#getting-started): Get started with Loki by doing first-run setup steps.
|
||||
* [Sharing Configurations](https://github.com/Dark-Alex-17/loki/wiki/Sharing-Configurations): Install bundles of agents, roles, macros, tools, and MCP servers from any git repo, and share your own.
|
||||
* [REPL](https://github.com/Dark-Alex-17/loki/wiki/REPL): Interactive Read-Eval-Print Loop for conversational interactions with LLMs and Loki.
|
||||
* [Custom REPL Prompt](https://github.com/Dark-Alex-17/loki/wiki/REPL-Prompt): Customize the REPL prompt to provide useful contextual information.
|
||||
* [Vault](https://github.com/Dark-Alex-17/loki/wiki/Vault): Securely store and manage sensitive information such as API keys and credentials.
|
||||
* [Shell Integrations](https://github.com/Dark-Alex-17/loki/wiki/Shell-Integrations): Seamlessly integrate Loki with your shell environment for enhanced command-line assistance.
|
||||
* [Function Calling](https://github.com/Dark-Alex-17/loki/wiki/Tools): Leverage function calling capabilities to extend Loki's functionality with custom tools
|
||||
* [Creating Custom Tools](https://github.com/Dark-Alex-17/loki/wiki/Custom-Tools): You can create your own custom tools to enhance Loki's capabilities.
|
||||
* [Create Custom Python Tools](https://github.com/Dark-Alex-17/loki/wiki/Custom-Tools#custom-python-based-tools)
|
||||
* [Create Custom TypeScript Tools](https://github.com/Dark-Alex-17/loki/wiki/Custom-Tools#custom-typescript-based-tools)
|
||||
* [Create Custom Bash Tools](https://github.com/Dark-Alex-17/loki/wiki/Custom-Bash-Tools)
|
||||
* [Bash Prompt Utilities](https://github.com/Dark-Alex-17/loki/wiki/Bash-Prompt-Helpers)
|
||||
* [First-Class MCP Server Support](https://github.com/Dark-Alex-17/loki/wiki/MCP-Servers): Easily connect and interact with MCP servers for advanced functionality.
|
||||
* [Macros](https://github.com/Dark-Alex-17/loki/wiki/Macros): Automate repetitive tasks and workflows with Loki "scripts" (macros).
|
||||
* [RAG](https://github.com/Dark-Alex-17/loki/wiki/RAG): Retrieval-Augmented Generation for enhanced information retrieval and generation.
|
||||
* [Sessions](https://github.com/Dark-Alex-17/loki/wiki/Sessions): Manage and persist conversational contexts and settings across multiple interactions.
|
||||
* [Roles](https://github.com/Dark-Alex-17/loki/wiki/Roles): Customize model behavior for specific tasks or domains.
|
||||
* [Agents](https://github.com/Dark-Alex-17/loki/wiki/Agents): Leverage AI agents to perform complex tasks and workflows, including sub-agent spawning, teammate messaging, and user interaction tools.
|
||||
* [Graph Agents](https://github.com/Dark-Alex-17/loki/wiki/Graph-Agents): Define an agent as a declarative, YAML-driven workflow. A directed graph of typed nodes (LLM calls, scripts, approvals, user input, RAG retrieval, sub-agent spawns).
|
||||
* [Todo System](https://github.com/Dark-Alex-17/loki/wiki/TODO-System): Built-in task tracking for improved LLM reliability with smaller models.
|
||||
* [Environment Variables](https://github.com/Dark-Alex-17/loki/wiki/Environment-Variables): Override and customize your Loki configuration at runtime with environment variables.
|
||||
* [Client Configurations](https://github.com/Dark-Alex-17/loki/wiki/Clients): Configuration instructions for various LLM providers.
|
||||
* [Authentication (API Key & OAuth)](https://github.com/Dark-Alex-17/loki/wiki/Clients#authentication): Authenticate with API keys or OAuth for subscription-based access.
|
||||
* [Patching API Requests](https://github.com/Dark-Alex-17/loki/wiki/Patches): Learn how to patch API requests for advanced customization.
|
||||
* [Custom Themes](https://github.com/Dark-Alex-17/loki/wiki/Themes): Change the look and feel of Loki to your preferences with custom themes.
|
||||
* [History](#history): A history of how Loki came to be.
|
||||
* [AIChat Migration Guide](https://github.com/Dark-Alex-17/coyote/wiki/AIChat-Migration): Coming from AIChat? Follow the migration guide to get started.
|
||||
* [Installation](#install): Install Coyote
|
||||
* [Getting Started](#getting-started): Get started with Coyote by doing first-run setup steps.
|
||||
* [Sharing Configurations](https://github.com/Dark-Alex-17/coyote/wiki/Sharing-Configurations): Install bundles of agents, roles, macros, tools, and MCP servers from any git repo, and share your own.
|
||||
* [REPL](https://github.com/Dark-Alex-17/coyote/wiki/REPL): Interactive Read-Eval-Print Loop for conversational interactions with LLMs and Coyote.
|
||||
* [Custom REPL Prompt](https://github.com/Dark-Alex-17/coyote/wiki/REPL-Prompt): Customize the REPL prompt to provide useful contextual information.
|
||||
* [Vault](https://github.com/Dark-Alex-17/coyote/wiki/Vault): Securely store and manage sensitive information such as API keys and credentials.
|
||||
* [Shell Integrations](https://github.com/Dark-Alex-17/coyote/wiki/Shell-Integrations): Seamlessly integrate Coyote with your shell environment for enhanced command-line assistance.
|
||||
* [Function Calling](https://github.com/Dark-Alex-17/coyote/wiki/Tools): Leverage function calling capabilities to extend Coyote's functionality with custom tools
|
||||
* [Creating Custom Tools](https://github.com/Dark-Alex-17/coyote/wiki/Custom-Tools): You can create your own custom tools to enhance Coyote's capabilities.
|
||||
* [Create Custom Python Tools](https://github.com/Dark-Alex-17/coyote/wiki/Custom-Tools#custom-python-based-tools)
|
||||
* [Create Custom TypeScript Tools](https://github.com/Dark-Alex-17/coyote/wiki/Custom-Tools#custom-typescript-based-tools)
|
||||
* [Create Custom Bash Tools](https://github.com/Dark-Alex-17/coyote/wiki/Custom-Bash-Tools)
|
||||
* [Bash Prompt Utilities](https://github.com/Dark-Alex-17/coyote/wiki/Bash-Prompt-Helpers)
|
||||
* [First-Class MCP Server Support](https://github.com/Dark-Alex-17/coyote/wiki/MCP-Servers): Easily connect and interact with MCP servers for advanced functionality.
|
||||
* [Macros](https://github.com/Dark-Alex-17/coyote/wiki/Macros): Automate repetitive tasks and workflows with Coyote "scripts" (macros).
|
||||
* [RAG](https://github.com/Dark-Alex-17/coyote/wiki/RAG): Retrieval-Augmented Generation for enhanced information retrieval and generation.
|
||||
* [Sessions](https://github.com/Dark-Alex-17/coyote/wiki/Sessions): Manage and persist conversational contexts and settings across multiple interactions.
|
||||
* [Roles](https://github.com/Dark-Alex-17/coyote/wiki/Roles): Customize model behavior for specific tasks or domains.
|
||||
* [Agents](https://github.com/Dark-Alex-17/coyote/wiki/Agents): Leverage AI agents to perform complex tasks and workflows, including sub-agent spawning, teammate messaging, and user interaction tools.
|
||||
* [Graph Agents](https://github.com/Dark-Alex-17/coyote/wiki/Graph-Agents): Define an agent as a declarative, YAML-driven workflow. A directed graph of typed nodes (LLM calls, scripts, approvals, user input, RAG retrieval, sub-agent spawns).
|
||||
* [Todo System](https://github.com/Dark-Alex-17/coyote/wiki/TODO-System): Built-in task tracking for improved LLM reliability with smaller models.
|
||||
* [Environment Variables](https://github.com/Dark-Alex-17/coyote/wiki/Environment-Variables): Override and customize your Coyote configuration at runtime with environment variables.
|
||||
* [Client Configurations](https://github.com/Dark-Alex-17/coyote/wiki/Clients): Configuration instructions for various LLM providers.
|
||||
* [Authentication (API Key & OAuth)](https://github.com/Dark-Alex-17/coyote/wiki/Clients#authentication): Authenticate with API keys or OAuth for subscription-based access.
|
||||
* [Patching API Requests](https://github.com/Dark-Alex-17/coyote/wiki/Patches): Learn how to patch API requests for advanced customization.
|
||||
* [Custom Themes](https://github.com/Dark-Alex-17/coyote/wiki/Themes): Change the look and feel of Coyote to your preferences with custom themes.
|
||||
* [History](#history): A history of how Coyote came to be.
|
||||
|
||||
## Prerequisites
|
||||
Loki requires the following tools to be installed on your system:
|
||||
Coyote requires the following tools to be installed on your system:
|
||||
* [jq](https://github.com/jqlang/jq)
|
||||
* `brew install jq`
|
||||
* [usql](https://github.com/xo/usql) (For the `sql` agent)
|
||||
@@ -57,57 +57,57 @@ Loki requires the following tools to be installed on your system:
|
||||
* [uv](https://docs.astral.sh/uv/getting-started/installation/)
|
||||
* `curl -LsSf https://astral.sh/uv/install.sh | sh`
|
||||
|
||||
These tools are used to provide various functionalities within Loki, such as document processing, JSON manipulation,
|
||||
These tools are used to provide various functionalities within Coyote, such as document processing, JSON manipulation,
|
||||
etc., and they are used within agents and tools.
|
||||
|
||||
## Install
|
||||
|
||||
### Cargo
|
||||
If you have Cargo installed, then you can install `loki` from Crates.io:
|
||||
If you have Cargo installed, then you can install `coyote` from Crates.io:
|
||||
|
||||
```shell
|
||||
cargo install loki-ai # Binary name is `loki`
|
||||
cargo install coyote-ai # Binary name is `coyote`
|
||||
|
||||
# If you encounter issues installing, try installing with '--locked'
|
||||
cargo install --locked loki-ai
|
||||
cargo install --locked coyote-ai
|
||||
```
|
||||
|
||||
### Homebrew (Mac/Linux)
|
||||
To install Loki from Homebrew, install the `loki` tap. Then you'll be able to install `loki`:
|
||||
To install Coyote from Homebrew, install the `coyote` tap. Then you'll be able to install `coyote`:
|
||||
|
||||
```shell
|
||||
brew tap Dark-Alex-17/loki
|
||||
brew install loki
|
||||
brew tap Dark-Alex-17/coyote
|
||||
brew install coyote
|
||||
|
||||
# If you need to be more specific, use:
|
||||
brew install Dark-Alex-17/loki/loki
|
||||
brew install Dark-Alex-17/coyote/coyote
|
||||
```
|
||||
|
||||
To upgrade `loki` using Homebrew:
|
||||
To upgrade `coyote` using Homebrew:
|
||||
|
||||
```shell
|
||||
brew upgrade loki
|
||||
brew upgrade coyote
|
||||
```
|
||||
|
||||
### Scripts
|
||||
#### Linux/MacOS (`bash`)
|
||||
You can use the following command to run a bash script that downloads and installs the latest version of `loki` for your
|
||||
You can use the following command to run a bash script that downloads and installs the latest version of `coyote` for your
|
||||
OS (Linux/MacOS) and architecture (x86_64/arm64):
|
||||
|
||||
```shell
|
||||
curl -fsSL https://raw.githubusercontent.com/Dark-Alex-17/loki/main/install_loki.sh | bash
|
||||
curl -fsSL https://raw.githubusercontent.com/Dark-Alex-17/coyote/main/install_coyote.sh | bash
|
||||
```
|
||||
|
||||
#### Windows/Linux/MacOS (`PowerShell`)
|
||||
You can use the following command to run a PowerShell script that downloads and installs the latest version of `loki`
|
||||
You can use the following command to run a PowerShell script that downloads and installs the latest version of `coyote`
|
||||
for your OS (Windows/Linux/MacOS) and architecture (x86_64/arm64):
|
||||
|
||||
```powershell
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -Command "iwr -useb https://raw.githubusercontent.com/Dark-Alex-17/loki/main/scripts/install_loki.ps1 | iex"
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -Command "iwr -useb https://raw.githubusercontent.com/Dark-Alex-17/coyote/main/scripts/install_coyote.ps1 | iex"
|
||||
```
|
||||
|
||||
### Manual
|
||||
Binaries are available on the [releases](https://github.com/Dark-Alex-17/loki/releases) page for the following platforms:
|
||||
Binaries are available on the [releases](https://github.com/Dark-Alex-17/coyote/releases) page for the following platforms:
|
||||
|
||||
| Platform | Architecture(s) |
|
||||
|----------------|-----------------|
|
||||
@@ -118,102 +118,103 @@ Binaries are available on the [releases](https://github.com/Dark-Alex-17/loki/re
|
||||
#### Windows Instructions
|
||||
To use a binary from the releases page on Windows, do the following:
|
||||
|
||||
1. Download the latest [binary](https://github.com/Dark-Alex-17/loki/releases) for your OS.
|
||||
1. Download the latest [binary](https://github.com/Dark-Alex-17/coyote/releases) for your OS.
|
||||
2. Use 7-Zip or TarTool to unpack the Tar file.
|
||||
3. Run the executable `loki.exe`!
|
||||
3. Run the executable `coyote.exe`!
|
||||
|
||||
#### Linux/MacOS Instructions
|
||||
To use a binary from the releases page on Linux/MacOS, do the following:
|
||||
|
||||
1. Download the latest [binary](https://github.com/Dark-Alex-17/loki/releases) for your OS.
|
||||
1. Download the latest [binary](https://github.com/Dark-Alex-17/coyote/releases) for your OS.
|
||||
2. `cd` to the directory where you downloaded the binary.
|
||||
3. Extract the binary with `tar -C /usr/local/bin -xzf loki-<arch>.tar.gz` (Note: This may require `sudo`)
|
||||
4. Now you can run `loki`!
|
||||
3. Extract the binary with `tar -C /usr/local/bin -xzf coyote-<arch>.tar.gz` (Note: This may require `sudo`)
|
||||
4. Now you can run `coyote`!
|
||||
|
||||
## Updating
|
||||
Loki can update itself in place to the latest GitHub release. Run `loki --update`
|
||||
for the newest release, or `loki --update v0.4.0` for a specific version:
|
||||
Coyote can update itself in place to the latest GitHub release. Run `coyote --update`
|
||||
for the newest release, or `coyote --update v0.4.0` for a specific version:
|
||||
|
||||
```shell
|
||||
loki --update
|
||||
loki --update v0.4.0
|
||||
coyote --update
|
||||
coyote --update v0.4.0
|
||||
```
|
||||
|
||||
The same is available from within the REPL via `.update` and `.update v0.4.0`.
|
||||
|
||||
If Loki was installed with a package manager, prefer that package manager so its
|
||||
records stay in sync with the binary on disk; i.e. `brew upgrade loki` for Homebrew,
|
||||
or `cargo install --locked loki-ai` for Cargo.
|
||||
If Coyote was installed with a package manager, prefer that package manager so its
|
||||
records stay in sync with the binary on disk; i.e. `brew upgrade coyote` for Homebrew,
|
||||
or `cargo install --locked coyote-ai` for Cargo.
|
||||
|
||||
When Loki detects a package-manager install it prints a warning and asks for
|
||||
When Coyote detects a package-manager install it prints a warning and asks for
|
||||
confirmation. In a non-interactive shell (no TTY), pass `--force` to update
|
||||
anyway:
|
||||
|
||||
```shell
|
||||
loki --update --force
|
||||
coyote --update --force
|
||||
```
|
||||
|
||||
## Getting Started
|
||||
After installation, you can generate the configuration files and directories by simply running:
|
||||
|
||||
```sh
|
||||
loki --info
|
||||
coyote --info
|
||||
```
|
||||
|
||||
Then, you need to set up the Loki vault by creating a vault password file. Loki will do this for you automatically and
|
||||
Then, you need to set up the Coyote vault by creating a vault password file. Coyote will do this for you automatically and
|
||||
guide you through the process when you first attempt to access the vault. So, to get started, you can run:
|
||||
|
||||
```sh
|
||||
loki --list-secrets
|
||||
coyote --list-secrets
|
||||
```
|
||||
|
||||
### Authentication
|
||||
Each client in your configuration needs authentication (with a few exceptions; e.g. ollama). Most clients use an API key
|
||||
(set via `api_key` in the config or through the [vault](https://github.com/Dark-Alex-17/loki/wiki/Vault)). For providers that support OAuth (e.g. Claude Pro/Max
|
||||
(set via `api_key` in the config or through the [vault](https://github.com/Dark-Alex-17/coyote/wiki/Vault)). For providers that support OAuth (e.g. Claude Pro/Max
|
||||
subscribers, Google Gemini), you can authenticate with your existing subscription instead:
|
||||
|
||||
```yaml
|
||||
# In your config.yaml
|
||||
clients:
|
||||
- type: claude
|
||||
name: my-claude-oauth
|
||||
auth: oauth # Indicate you want to authenticate with OAuth instead of an API key
|
||||
```
|
||||
|
||||
```sh
|
||||
loki --authenticate claude
|
||||
coyote --authenticate my-claude-oauth
|
||||
# Or via the REPL: .authenticate
|
||||
```
|
||||
|
||||
For full details, see the [authentication documentation](https://github.com/Dark-Alex-17/loki/wiki/Clients#authentication).
|
||||
For full details, see the [authentication documentation](https://github.com/Dark-Alex-17/coyote/wiki/Clients#authentication).
|
||||
|
||||
### Tab-Completions
|
||||
You can also enable tab completions to make using Loki easier. To do so, add the following to your shell profile:
|
||||
You can also enable tab completions to make using Coyote easier. To do so, add the following to your shell profile:
|
||||
```shell
|
||||
# Bash
|
||||
# (add to: `~/.bashrc`)
|
||||
source <(COMPLETE=bash loki)
|
||||
source <(COMPLETE=bash coyote)
|
||||
|
||||
# Zsh
|
||||
# (add to: `~/.zshrc`)
|
||||
source <(COMPLETE=zsh loki)
|
||||
source <(COMPLETE=zsh coyote)
|
||||
|
||||
# Fish
|
||||
# (add to: `~/.config/fish/config.fish`)
|
||||
source <(COMPLETE=fish loki | psub)
|
||||
source <(COMPLETE=fish coyote | psub)
|
||||
|
||||
# Elvish
|
||||
# (add to: `~/.elvish/rc.elv`)
|
||||
eval (E:COMPLETE=elvish loki | slurp)
|
||||
eval (E:COMPLETE=elvish coyote | slurp)
|
||||
|
||||
# PowerShell
|
||||
# (add to: `$PROFILE`)
|
||||
$env:COMPLETE = "powershell"
|
||||
loki | Out-String | Invoke-Expression
|
||||
coyote | Out-String | Invoke-Expression
|
||||
```
|
||||
|
||||
### Shell Integration
|
||||
You can integrate Loki's Shell Assistant into your shell for enhanced command-line assistance. Add the code in the
|
||||
corresponding [shell integration script](./scripts/shell-integration) to your shell. Then, you can invoke Loki to convert natural language to
|
||||
You can integrate Coyote's Shell Assistant into your shell for enhanced command-line assistance. Add the code in the
|
||||
corresponding [shell integration script](./scripts/shell-integration) to your shell. Then, you can invoke Coyote to convert natural language to
|
||||
shell commands by pressing `Alt-e`. For example:
|
||||
|
||||
```shell
|
||||
@@ -223,18 +224,18 @@ find . -name "*.md"
|
||||
```
|
||||
|
||||
## Configuration
|
||||
The location of the global Loki configuration varies between systems, so you can use the following command to find your
|
||||
The location of the global Coyote configuration varies between systems, so you can use the following command to find your
|
||||
`config.yaml` file:
|
||||
|
||||
```shell
|
||||
loki --info | grep 'config_file' | awk '{print $2}'
|
||||
coyote --info | grep 'config_file' | awk '{print $2}'
|
||||
```
|
||||
|
||||
The configuration file consists of a number of settings. To see a full example configuration file with every setting
|
||||
defined, refer to the [example configuration file](./config.example.yaml).
|
||||
|
||||
### Default LLM
|
||||
The following settings are available to configure the default LLM that is used when you start Loki, and its
|
||||
The following settings are available to configure the default LLM that is used when you start Coyote, and its
|
||||
hyperparameters:
|
||||
|
||||
| Setting | Description |
|
||||
@@ -244,34 +245,34 @@ hyperparameters:
|
||||
| `top_p` | The default `top_p` hyperparameter value to use for all models, with a range of (0,1) (or (0,2) for some models); <br>Used unless explicitly overridden |
|
||||
|
||||
### CLI Behavior
|
||||
You can use the following settings to modify the behavior of Loki:
|
||||
You can use the following settings to modify the behavior of Coyote:
|
||||
|
||||
| Setting | Default Value | Description |
|
||||
|---------------|---------------|-------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `stream` | `true` | Controls whether to use stream-style APIs when querying for completions from LLM providers |
|
||||
| `save` | `true` | Controls whether to save each query/response to every model to `messages.md` for posterity; Useful for debugging |
|
||||
| `keybindings` | `emacs` | Specifies which keybinding schema to use; can either be `emacs` or `vi` |
|
||||
| `editor` | `null` | What text editor Loki should use to edit the input buffer or session (e.g. `vim`, `emacs`, `nano`, `hx`); <br>Defaults to `$EDITOR` |
|
||||
| `editor` | `null` | What text editor Coyote should use to edit the input buffer or session (e.g. `vim`, `emacs`, `nano`, `hx`); <br>Defaults to `$EDITOR` |
|
||||
| `wrap` | `no` | Controls whether text is wrapped (can be `no`, `auto`, or some `<max_width>` |
|
||||
| `wrap_code` | `false` | Enables or disables the wrapping of code blocks |
|
||||
|
||||
### Preludes
|
||||
Preludes let you define the default behavior for the different operating modes of Loki. The available settings are
|
||||
Preludes let you define the default behavior for the different operating modes of Coyote. The available settings are
|
||||
shown below:
|
||||
|
||||
| Setting | Description |
|
||||
|-----------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `repl_prelude` | This setting lets you specify a default `session` or `role` to use when starting Loki in [REPL](https://github.com/Dark-Alex-17/loki/wiki/REPL) mode. <br>Values can be <ul><li>`role:<name>` to define a role</li><li>`session:<name>` to define a session</li><li>`<session>:<role>` to define both a session and a role to use</li></ul> |
|
||||
| `cmd_prelude` | This setting lets you specify a default `session` or `role` to use when running one-off queries in Loki via the CLI. <br>Values can be <ul><li>`role:<name>` to define a role</li><li>`session:<name>` to define a session</li><li>`<session>:<role>` to define both a session and a role to use</li></ul> |
|
||||
| `repl_prelude` | This setting lets you specify a default `session` or `role` to use when starting Coyote in [REPL](https://github.com/Dark-Alex-17/coyote/wiki/REPL) mode. <br>Values can be <ul><li>`role:<name>` to define a role</li><li>`session:<name>` to define a session</li><li>`<session>:<role>` to define both a session and a role to use</li></ul> |
|
||||
| `cmd_prelude` | This setting lets you specify a default `session` or `role` to use when running one-off queries in Coyote via the CLI. <br>Values can be <ul><li>`role:<name>` to define a role</li><li>`session:<name>` to define a session</li><li>`<session>:<role>` to define both a session and a role to use</li></ul> |
|
||||
| `agent_session` | This setting is used to specify a default session that all agents should start into, unless otherwise specified in the agent configuration. (e.g. `temp`, `default`) |
|
||||
|
||||
### Appearance
|
||||
The appearance of Loki can be modified using the following settings:
|
||||
The appearance of Coyote can be modified using the following settings:
|
||||
|
||||
| Setting | Default Value | Description |
|
||||
|---------------|---------------|------------------------------------------------------|
|
||||
| `highlight` | `true` | This setting enables or disables syntax highlighting |
|
||||
| `light_theme` | `false` | This setting toggles light mode in Loki |
|
||||
| `light_theme` | `false` | This setting toggles light mode in Coyote |
|
||||
|
||||
### Miscellaneous Settings
|
||||
| Setting | Default Value | Description |
|
||||
@@ -283,7 +284,7 @@ The appearance of Loki can be modified using the following settings:
|
||||
|
||||
## History
|
||||
|
||||
Loki began as a fork of [AIChat CLI](https://github.com/sigoden/aichat) and has since evolved into an independent project.
|
||||
Coyote began as a fork of [AIChat CLI](https://github.com/sigoden/aichat) and has since evolved into an independent project.
|
||||
|
||||
See [CREDITS.md](./CREDITS.md) for full attribution and background.
|
||||
|
||||
|
||||
@@ -7,14 +7,14 @@ set -euo pipefail
|
||||
#######################
|
||||
|
||||
# Cache file name for detected project info
|
||||
_LOKI_PROJECT_CACHE=".loki-project.json"
|
||||
_COYOTE_PROJECT_CACHE=".coyote-project.json"
|
||||
|
||||
# Read cached project detection if valid
|
||||
# Usage: _read_project_cache "/path/to/project"
|
||||
# Returns: cached JSON on stdout (exit 0) or nothing (exit 1)
|
||||
_read_project_cache() {
|
||||
local dir="$1"
|
||||
local cache_file="${dir}/${_LOKI_PROJECT_CACHE}"
|
||||
local cache_file="${dir}/${_COYOTE_PROJECT_CACHE}"
|
||||
|
||||
if [[ -f "${cache_file}" ]]; then
|
||||
local cached
|
||||
@@ -32,7 +32,7 @@ _read_project_cache() {
|
||||
_write_project_cache() {
|
||||
local dir="$1"
|
||||
local json="$2"
|
||||
local cache_file="${dir}/${_LOKI_PROJECT_CACHE}"
|
||||
local cache_file="${dir}/${_COYOTE_PROJECT_CACHE}"
|
||||
|
||||
echo "${json}" > "${cache_file}" 2>/dev/null || true
|
||||
}
|
||||
@@ -238,7 +238,7 @@ _detect_with_llm() {
|
||||
)
|
||||
|
||||
local llm_response
|
||||
llm_response=$(loki --no-stream "${prompt}" 2>/dev/null) || return 1
|
||||
llm_response=$(coyote --no-stream "${prompt}" 2>/dev/null) || return 1
|
||||
|
||||
llm_response=$(echo "${llm_response}" | sed 's/^```json//;s/^```//;s/```$//' | tr -d '\n' | sed 's/^[[:space:]]*//')
|
||||
llm_response=$(echo "${llm_response}" | grep -o '{[^}]*}' | head -1)
|
||||
|
||||
@@ -4,7 +4,7 @@ A graph-based implementation agent. Plans, implements, and runs build +
|
||||
tests in a bounded fix-loop until verified. Designed to be delegated to by
|
||||
the **[Sisyphus](../sisyphus/README.md)** agent.
|
||||
|
||||
Coder is a [graph agent](https://github.com/Dark-Alex-17/loki/wiki/Graph-Agents): its workflow is
|
||||
Coder is a [graph agent](https://github.com/Dark-Alex-17/coyote/wiki/Graph-Agents): its workflow is
|
||||
defined declaratively in `graph.yaml`, with verification and the
|
||||
implement-fix loop enforced as graph edges rather than prose.
|
||||
|
||||
@@ -42,10 +42,10 @@ so it accepts the runtime override flag:
|
||||
```sh
|
||||
# Invoke from inside the project (project_dir defaults to ".")
|
||||
cd /path/to/your/project
|
||||
loki -a coder "Add a foo() function..."
|
||||
coyote -a coder "Add a foo() function..."
|
||||
|
||||
# Or invoke from anywhere with an explicit override
|
||||
loki -a coder --agent-variable project_dir /path/to/your/project "Add..."
|
||||
coyote -a coder --agent-variable project_dir /path/to/your/project "Add..."
|
||||
```
|
||||
|
||||
`graph.yaml` `initial_state` exposes:
|
||||
|
||||
@@ -17,8 +17,8 @@ variables:
|
||||
- name: project_dir
|
||||
description: |
|
||||
Absolute path to the project directory. Defaults to "." which is the
|
||||
directory you invoked `loki` from. Override at runtime with
|
||||
`loki -a coder --agent-variable project_dir /abs/path "..."`.
|
||||
directory you invoked `coyote` from. Override at runtime with
|
||||
`coyote -a coder --agent-variable project_dir /abs/path "..."`.
|
||||
default: "."
|
||||
|
||||
settings:
|
||||
@@ -70,7 +70,7 @@ nodes:
|
||||
MUST be absolute. The project root is {{project_dir}}. Prefer paths
|
||||
like "{{project_dir}}/src/foo.rs" over "src/foo.rs". The implementer
|
||||
uses these paths directly with fs_write and fs_patch tools, which
|
||||
resolve relative paths against the loki invocation directory (NOT
|
||||
resolve relative paths against the coyote invocation directory (NOT
|
||||
the project dir). Empty arrays are fine if no files in that category.
|
||||
|
||||
`risks` is a list of short strings. Anything that could derail the
|
||||
@@ -155,7 +155,7 @@ nodes:
|
||||
2. Use `fs_write` for new files or full rewrites.
|
||||
3. NEVER output code to chat. Always use tools.
|
||||
4. ALWAYS pass ABSOLUTE paths to fs_write and fs_patch. Relative
|
||||
paths resolve against the loki invocation directory (not the
|
||||
paths resolve against the coyote invocation directory (not the
|
||||
project dir), which is rarely what you want. The project root
|
||||
is {{project_dir}}.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# deep-research
|
||||
|
||||
A deep web research agent, built as a Loki graph agent. It plans an
|
||||
A deep web research agent, built as a Coyote graph agent. It plans an
|
||||
investigation, decomposes it into sub-questions researched in
|
||||
parallel, grounds the work in a local knowledge corpus, vets the
|
||||
credibility of cited sources, runs a reflexion self-critique loop to
|
||||
@@ -13,12 +13,12 @@ this agent runs a fixed graph: every request goes through the same
|
||||
`plan -> parallel research -> vet -> critique -> synthesize -> verify -> approve`
|
||||
pipeline.
|
||||
|
||||
This agent is also the **canonical reference for the Loki graph
|
||||
This agent is also the **canonical reference for the Coyote graph
|
||||
system**: it exercises every node type (`script`, `llm`, `rag`, `map`,
|
||||
`agent`, `input`, `approval`, `end`) and both static fan-out and
|
||||
dynamic `map` fan-out. If you are learning how to build a graph
|
||||
agent, this is the file to read alongside the
|
||||
[Graph-Agents wiki](https://github.com/Dark-Alex-17/loki/wiki/Graph-Agents).
|
||||
[Graph-Agents wiki](https://github.com/Dark-Alex-17/coyote/wiki/Graph-Agents).
|
||||
|
||||
## Workflow
|
||||
|
||||
@@ -49,7 +49,7 @@ incorporate_feedback (script) -> research_each_question (the human-feedbac
|
||||
### Node-type breakdown
|
||||
|
||||
| Type | Nodes |
|
||||
|---|---|
|
||||
|-----------------------------|-----------------------------------------------------------------------------------------------------------------------|
|
||||
| `script` (Python) | `parse_request`, `bootstrap_research`, `combine_findings`, `reflexion_gate`, `verify_sources`, `incorporate_feedback` |
|
||||
| `llm` (tools: `[]`) | `plan`, `critique` |
|
||||
| `llm` (with tool whitelist) | `research_one_question`, `vet_sources` |
|
||||
@@ -62,7 +62,7 @@ incorporate_feedback (script) -> research_each_question (the human-feedbac
|
||||
|
||||
## Parallel execution
|
||||
|
||||
The graph has two parallel super-steps where Loki's BSP scheduler runs
|
||||
The graph has two parallel super-steps where Coyote's BSP scheduler runs
|
||||
branches concurrently.
|
||||
|
||||
**1. Context loading (`plan` ‖ `knowledge_lookup`)** — after
|
||||
@@ -96,7 +96,7 @@ PDFs, or text files into `knowledge/` to bias the research toward
|
||||
your local context.
|
||||
|
||||
The knowledge base is built once, at agent-load time, into
|
||||
`~/.config/loki/agents/deep-research/knowledge_lookup.yaml`. Because
|
||||
`~/.config/coyote/agents/deep-research/knowledge_lookup.yaml`. Because
|
||||
the node fully specifies its build config (`embedding_model`,
|
||||
`chunk_size`, `chunk_overlap`), the build is non-interactive. Delete
|
||||
that cached file after adding or changing knowledge to force a
|
||||
@@ -119,13 +119,13 @@ for details.
|
||||
|
||||
## Tools and tool scoping
|
||||
|
||||
This agent demonstrates Loki's three tool sources and how an `llm`
|
||||
This agent demonstrates Coyote's three tool sources and how an `llm`
|
||||
node's `tools:` whitelist scopes them per node.
|
||||
|
||||
The agent's full tool universe, declared in `graph.yaml`:
|
||||
|
||||
- **Global tools** (`global_tools`): `web_search_loki`,
|
||||
`fetch_url_via_curl`, `search_arxiv` - Loki's built-in tool scripts.
|
||||
- **Global tools** (`global_tools`): `web_search_coyote`,
|
||||
`fetch_url_via_curl`, `search_arxiv` - Coyote's built-in tool scripts.
|
||||
- **MCP server** (`mcp_servers`): `ddg-search` - a DuckDuckGo web
|
||||
search MCP server. Referenced in a whitelist as `mcp:ddg-search`.
|
||||
- **Custom agent tool** (`tools.sh`): `classify_source` - a
|
||||
@@ -135,9 +135,9 @@ No node receives all of these. Each `llm` node's `tools:` whitelist
|
||||
narrows the universe to exactly what that step needs:
|
||||
|
||||
| Node | `tools:` whitelist | Draws from |
|
||||
|---|---|---|
|
||||
|-------------------------|-----------------------------------------------------------------------------|--------------------------|
|
||||
| `plan`, `critique` | `[]` | nothing - pure reasoning |
|
||||
| `research_one_question` | `web_search_loki`, `fetch_url_via_curl`, `search_arxiv`, `mcp:ddg-search` | global tools + MCP |
|
||||
| `research_one_question` | `web_search_coyote`, `fetch_url_via_curl`, `search_arxiv`, `mcp:ddg-search` | global tools + MCP |
|
||||
| `vet_sources` | `classify_source` | the custom tool only |
|
||||
|
||||
`research_one_question` (each parallel branch of the map) can search
|
||||
@@ -153,21 +153,21 @@ deterministic - exactly the kind of logic a tool should own rather than
|
||||
the LLM guessing.
|
||||
|
||||
Web search may require API-key configuration; see the
|
||||
[Tools](https://github.com/Dark-Alex-17/loki/wiki/Tools) docs.
|
||||
[Tools](https://github.com/Dark-Alex-17/coyote/wiki/Tools) docs.
|
||||
`fetch_url_via_curl`, `search_arxiv`, and `classify_source` work
|
||||
without a key.
|
||||
|
||||
## Setup
|
||||
|
||||
`research_one_question` (each parallel branch of the `map`) uses the
|
||||
`ddg-search` MCP server via `mcp:ddg-search`. It is one of Loki's
|
||||
`ddg-search` MCP server via `mcp:ddg-search`. It is one of Coyote's
|
||||
default MCP servers; make sure it is registered in
|
||||
`~/.config/loki/mcp.json` (run `loki --install mcp_config` to restore
|
||||
`~/.config/coyote/mcp.json` (run `coyote --install mcp_config` to restore
|
||||
the default template if it is missing). If `ddg-search` is unavailable,
|
||||
the branches still have their global web-search tools to fall back on.
|
||||
|
||||
The `synthesize` node spawns the `report-writer` sub-agent. Both
|
||||
agents ship with `loki agents install`; if you install one manually,
|
||||
agents ship with `coyote agents install`; if you install one manually,
|
||||
install both so the agent reference resolves.
|
||||
|
||||
## Reflexion
|
||||
@@ -205,10 +205,10 @@ backstop: it caps the total visits to any single node.
|
||||
## Running
|
||||
|
||||
```sh
|
||||
loki agents install # ships deep-research
|
||||
loki -a deep-research "How does HTTP/3 differ from HTTP/2?"
|
||||
loki -a deep-research "Recent advances in solid-state batteries"
|
||||
loki -a deep-research # no prompt -> triggers ask_topic
|
||||
coyote agents install # ships deep-research
|
||||
coyote -a deep-research "How does HTTP/3 differ from HTTP/2?"
|
||||
coyote -a deep-research "Recent advances in solid-state batteries"
|
||||
coyote -a deep-research # no prompt -> triggers ask_topic
|
||||
```
|
||||
|
||||
## Anti-hallucination
|
||||
@@ -240,7 +240,7 @@ loki -a deep-research # no prompt -> triggers ask_topic
|
||||
`report-writer` sub-agent.
|
||||
- **Tool scope.** Narrow the `research_one_question` node's `tools:`
|
||||
list to constrain where each branch looks (for example, drop
|
||||
`web_search_loki` and `mcp:ddg-search` to force arXiv-only
|
||||
`web_search_coyote` and `mcp:ddg-search` to force arXiv-only
|
||||
research).
|
||||
- **Local knowledge.** Drop files into `knowledge/` to bias every
|
||||
research branch toward your local context (see the *Local
|
||||
|
||||
@@ -9,7 +9,7 @@ description: |
|
||||
approval. A reviewer's free-form feedback at the approval step feeds
|
||||
back into another research pass.
|
||||
|
||||
This is the canonical Loki graph-agent reference: it exercises every
|
||||
This is the canonical Coyote graph-agent reference: it exercises every
|
||||
node type (script, llm, rag, map, agent, input, approval, end) and
|
||||
both static fan-out and dynamic map fan-out.
|
||||
|
||||
@@ -18,7 +18,7 @@ version: "1.0"
|
||||
temperature: 0.0
|
||||
|
||||
global_tools:
|
||||
- web_search_loki.sh
|
||||
- web_search_coyote.sh
|
||||
- fetch_url_via_curl.sh
|
||||
- search_arxiv.sh
|
||||
|
||||
@@ -147,7 +147,7 @@ nodes:
|
||||
|
||||
{{research_feedback}}
|
||||
tools:
|
||||
- web_search_loki
|
||||
- web_search_coyote
|
||||
- fetch_url_via_curl
|
||||
- search_arxiv
|
||||
- mcp:ddg-search
|
||||
|
||||
@@ -5,7 +5,7 @@ hybrid (vector + keyword) retrieval over every file in this directory.
|
||||
Drop your own notes, papers (PDFs), Markdown docs, or text files here
|
||||
and they will be indexed into a per-agent knowledge base on first run.
|
||||
|
||||
Loki supports common file types out of the box: `.md`, `.txt`, `.pdf`,
|
||||
Coyote supports common file types out of the box: `.md`, `.txt`, `.pdf`,
|
||||
`.html`, and others. Subdirectories are walked recursively.
|
||||
|
||||
A small starter file (`research-style-notes.md`) ships so the RAG
|
||||
@@ -17,7 +17,7 @@ To force the knowledge base to rebuild after you add or change files,
|
||||
delete the cached index:
|
||||
|
||||
```sh
|
||||
rm ~/.config/loki/agents/deep-research/knowledge_lookup.yaml
|
||||
rm ~/.config/coyote/agents/deep-research/knowledge_lookup.yaml
|
||||
```
|
||||
|
||||
The next run will rebuild from the current contents of this directory.
|
||||
|
||||
@@ -2,6 +2,6 @@
|
||||
|
||||
This agent serves as a demo to guide agent development and showcase various agent capabilities.
|
||||
|
||||
To enable tools, Loki will look for the first `tools.py` or `tools.sh` file it finds in this directory.
|
||||
To enable tools, Coyote will look for the first `tools.py` or `tools.sh` file it finds in this directory.
|
||||
|
||||
The base configuration using `tools.py`. To switch to using `tools.sh`, rename or remove `tools.py`.
|
||||
|
||||
@@ -17,7 +17,7 @@ It can also be used as a standalone tool for understanding codebases and finding
|
||||
## Pro-Tip: Use an IDE MCP Server for Improved Performance
|
||||
Many modern IDEs now include MCP servers that let LLMs perform operations within the IDE itself and use IDE tools. Using
|
||||
an IDE's MCP server dramatically improves the performance of coding agents. So if you have an IDE, try adding that MCP
|
||||
server to your config (see the [MCP Server docs](../../../docs/function-calling/MCP-SERVERS.md) to see how to configure
|
||||
server to your config (see the [MCP Server docs](https://github.com/Dark-Alex-17/loki/wiki/MCP-Servers) to see how to configure
|
||||
them), and modify the agent definition to look like this:
|
||||
|
||||
```yaml
|
||||
@@ -31,7 +31,7 @@ global_tools:
|
||||
- fs_grep.sh
|
||||
- fs_glob.sh
|
||||
- fs_ls.sh
|
||||
- web_search_loki.sh
|
||||
- web_search_coyote.sh
|
||||
|
||||
# ...
|
||||
```
|
||||
|
||||
@@ -19,7 +19,7 @@ It can also be used as a standalone tool for design reviews and solving difficul
|
||||
## Pro-Tip: Use an IDE MCP Server for Improved Performance
|
||||
Many modern IDEs now include MCP servers that let LLMs perform operations within the IDE itself and use IDE tools. Using
|
||||
an IDE's MCP server dramatically improves the performance of coding agents. So if you have an IDE, try adding that MCP
|
||||
server to your config (see the [MCP Server docs](../../../docs/function-calling/MCP-SERVERS.md) to see how to configure
|
||||
server to your config (see the [MCP Server docs](https://github.com/Dark-Alex-17/loki/wiki/MCP-Servers) to see how to configure
|
||||
them), and modify the agent definition to look like this:
|
||||
|
||||
```yaml
|
||||
@@ -33,7 +33,7 @@ global_tools:
|
||||
- fs_grep.sh
|
||||
- fs_glob.sh
|
||||
- fs_ls.sh
|
||||
- web_search_loki.sh
|
||||
- web_search_coyote.sh
|
||||
|
||||
# ...
|
||||
```
|
||||
|
||||
@@ -27,7 +27,7 @@ You can also use this agent directly if you have a set of findings you
|
||||
want polished:
|
||||
|
||||
```sh
|
||||
loki -a report-writer "Topic: X. Findings: <paste findings here>"
|
||||
coyote -a report-writer "Topic: X. Findings: <paste findings here>"
|
||||
```
|
||||
|
||||
It will produce a single Markdown report following the rules in its
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Sisyphus
|
||||
|
||||
The main coordinator agent for the Loki coding ecosystem, providing a powerful CLI interface for code generation and
|
||||
The main coordinator agent for the Coyote coding ecosystem, providing a powerful CLI interface for code generation and
|
||||
project management similar to OpenCode, ClaudeCode, Codex, or Gemini CLI.
|
||||
|
||||
_Inspired by the Sisyphus and Oracle agents of OpenCode._
|
||||
@@ -19,8 +19,8 @@ Sisyphus acts as the primary entry point, capable of handling complex tasks by c
|
||||
|
||||
## Pro-Tip: Use an IDE MCP Server for Improved Performance
|
||||
Many modern IDEs (JetBrains, VS Code, Cursor, Zed, etc.) expose MCP servers that let LLMs use IDE tools directly. Using
|
||||
one dramatically improves the performance of coding agents. If you have one, add it to your loki config (see the
|
||||
[MCP Server docs](../../../docs/function-calling/MCP-SERVERS.md)) and reference it in this agent's `mcp_servers:` list:
|
||||
one dramatically improves the performance of coding agents. If you have one, add it to your coyote config (see the
|
||||
[MCP Server docs](https://github.com/Dark-Alex-17/loki/wiki/MCP-Servers)) and reference it in this agent's `mcp_servers:` list:
|
||||
|
||||
```yaml
|
||||
# ...
|
||||
@@ -33,7 +33,7 @@ global_tools:
|
||||
- fs_grep.sh
|
||||
- fs_glob.sh
|
||||
- fs_ls.sh
|
||||
- web_search_loki.sh
|
||||
- web_search_coyote.sh
|
||||
- execute_command.sh
|
||||
|
||||
# ...
|
||||
|
||||
-1106
File diff suppressed because it is too large
Load Diff
+6
-6
@@ -6,11 +6,11 @@ set -e
|
||||
|
||||
# @option --query! The search query.
|
||||
|
||||
# @meta require-tools loki
|
||||
# @meta require-tools coyote
|
||||
|
||||
# @env WEB_SEARCH_MODEL=gemini:gemini-2.5-flash The model for web-searching.
|
||||
#
|
||||
# supported loki models:
|
||||
# supported coyote models:
|
||||
# - gemini:gemini-2.0-*
|
||||
# - vertexai:gemini-*
|
||||
# - perplexity:*
|
||||
@@ -22,15 +22,15 @@ main() {
|
||||
client="${WEB_SEARCH_MODEL%%:*}"
|
||||
|
||||
if [[ "$client" == "gemini" ]]; then
|
||||
export LOKI_PATCH_GEMINI_CHAT_COMPLETIONS='{".*":{"body":{"tools":[{"google_search":{}}]}}}'
|
||||
export COYOTE_PATCH_GEMINI_CHAT_COMPLETIONS='{".*":{"body":{"tools":[{"google_search":{}}]}}}'
|
||||
elif [[ "$client" == "vertexai" ]]; then
|
||||
export LOKI_PATCH_VERTEXAI_CHAT_COMPLETIONS='{
|
||||
export COYOTE_PATCH_VERTEXAI_CHAT_COMPLETIONS='{
|
||||
"gemini-1.5-.*":{"body":{"tools":[{"googleSearchRetrieval":{}}]}},
|
||||
"gemini-2.0-.*":{"body":{"tools":[{"google_search":{}}]}}
|
||||
}'
|
||||
elif [[ "$client" == "ernie" ]]; then
|
||||
export LOKI_PATCH_ERNIE_CHAT_COMPLETIONS='{".*":{"body":{"web_search":{"enable":true}}}}'
|
||||
export COYOTE_PATCH_ERNIE_CHAT_COMPLETIONS='{".*":{"body":{"web_search":{"enable":true}}}}'
|
||||
fi
|
||||
|
||||
loki -m "$WEB_SEARCH_MODEL" "$argc_query" >> "$LLM_OUTPUT"
|
||||
coyote -m "$WEB_SEARCH_MODEL" "$argc_query" >> "$LLM_OUTPUT"
|
||||
}
|
||||
@@ -506,7 +506,6 @@ open_link() {
|
||||
}
|
||||
|
||||
guard_operation() {
|
||||
if [[ -t 1 ]]; then
|
||||
if [[ -z "$AUTO_CONFIRM" && -z "$LLM_AGENT_VAR_AUTO_CONFIRM" ]]; then
|
||||
ans="$(confirm "${1:-Are you sure you want to continue?}")"
|
||||
|
||||
@@ -515,7 +514,6 @@ guard_operation() {
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# Here is an example of a patch block that can be applied to modify the file to request the user's name:
|
||||
@@ -655,7 +653,6 @@ guard_path() {
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -t 1 ]]; then
|
||||
path="$(_to_real_path "$1")"
|
||||
confirmation_prompt="$2"
|
||||
|
||||
@@ -667,7 +664,6 @@ guard_path() {
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
_to_real_path() {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -9,7 +9,7 @@ security/configuration settings. The analysis aims to ensure a thorough understa
|
||||
structured and operates, enabling the creation of new files, maintaining consistency with existing practices, and the
|
||||
potential implementation of best practices.
|
||||
|
||||
Should the root directory contain a `LOKI.md` file, this was generated by Loki and should be used as a reference
|
||||
Should the root directory contain a `COYOTE.md` file, this was generated by Coyote and should be used as a reference
|
||||
point for all analysis, style questions, etc.
|
||||
|
||||
**Objective:** Enable the AI to thoroughly analyze a software repository, providing detailed insights and guidelines on
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Agent-specific configuration
|
||||
# Location `<loki-config-dir>/agents/<agent-name>/config.yaml`
|
||||
# Location `<coyote-config-dir>/agents/<agent-name>/config.yaml`
|
||||
#
|
||||
# Available Environment Variables:
|
||||
# - <agent-name>_MODEL
|
||||
@@ -21,14 +21,14 @@ version: 1 # Version of the agent
|
||||
# The auto-continue system provides built-in task tracking for improved reliability.
|
||||
# When enabled, the model can create todo lists and the system will automatically
|
||||
# prompt it to continue when incomplete tasks remain.
|
||||
# See the [Todo System documentation](https://github.com/Dark-Alex-17/loki/wiki/TODO-System) for more information
|
||||
# See the [Todo System documentation](https://github.com/Dark-Alex-17/coyote/wiki/TODO-System) for more information
|
||||
auto_continue: false # Enable automatic continuation when incomplete todos remain
|
||||
max_auto_continues: 10 # Maximum number of automatic continuations before stopping
|
||||
inject_todo_instructions: true # Inject the default todo tool usage instructions into the agent's system prompt
|
||||
continuation_prompt: null # Custom prompt used when auto-continuing (optional; uses default if null)
|
||||
# Sub-Agent Spawning System
|
||||
# Enable this agent to spawn and manage child agents in parallel.
|
||||
# See https://github.com/Dark-Alex-17/loki/wiki/Agents for detailed documentation.
|
||||
# See https://github.com/Dark-Alex-17/coyote/wiki/Agents for detailed documentation.
|
||||
can_spawn_agents: false # Enable the agent to spawn child agents
|
||||
max_concurrent_agents: 4 # Maximum number of agents that can run simultaneously
|
||||
max_agent_depth: 3 # Maximum nesting depth for sub-agents (prevents runaway spawning)
|
||||
@@ -37,7 +37,7 @@ summarization_model: null # Model to use for summarizing sub-agent output
|
||||
summarization_threshold: 4000 # Character threshold above which sub-agent output is summarized before returning to parent
|
||||
escalation_timeout: 300 # Seconds a sub-agent waits for a user interaction response before timing out (default: 5 minutes)
|
||||
mcp_servers: # Optional list of MCP servers that the agent utilizes
|
||||
- github # Corresponds to the name of an MCP server in the `<loki-config-dir>/functions/mcp.json` file
|
||||
- github # Corresponds to the name of an MCP server in the `<coyote-config-dir>/functions/mcp.json` file
|
||||
global_tools: # Optional list of additional global tools to enable for the agent; i.e. not tools specific to the agent
|
||||
- web_search
|
||||
- fs
|
||||
@@ -80,10 +80,10 @@ conversation_starters: # Optional conversation starters for the agent
|
||||
- What is the best way to exercise?
|
||||
- How do I manage my time effectively?
|
||||
documents: # Optional documents to load for the agent
|
||||
- git:/some/repo # Explicitly tell Loki to use the 'git' document loader using an absolute path
|
||||
- pdf:some-pdf-file.pdf # Explicitly tell Loki to use the 'pdf' document loader using a relative path
|
||||
- git:/some/repo # Explicitly tell Coyote to use the 'git' document loader using an absolute path
|
||||
- pdf:some-pdf-file.pdf # Explicitly tell Coyote to use the 'pdf' document loader using a relative path
|
||||
- https://some-website.com/some-page
|
||||
- some-file.pdf # File with relative path to the <loki-config-dir>/agents/<agent-name> directory; i.e. file in the same directory as this config file
|
||||
- some-file.pdf # File with relative path to the <coyote-config-dir>/agents/<agent-name> directory; i.e. file in the same directory as this config file
|
||||
- ~/some-file.txt # File in the user's home directory
|
||||
- /absolute/path/to/some-file.md # File with absolute path
|
||||
- /absolute/path/**/NAME.txt # Find all NAME.txt files in the specified directory and all its subdirectories
|
||||
|
||||
+46
-46
@@ -18,31 +18,31 @@ agent_session: null # Set a session to use when starting an agent (
|
||||
|
||||
# ---- Appearance ----
|
||||
highlight: true # Controls syntax highlighting
|
||||
light_theme: false # Activates a light color theme when true. env: LOKI_LIGHT_THEME
|
||||
light_theme: false # Activates a light color theme when true. env: COYOTE_LIGHT_THEME
|
||||
|
||||
# ---- Miscellaneous ----
|
||||
user_agent: null # Set User-Agent HTTP header, use `auto` for loki/<current-version>
|
||||
user_agent: null # Set User-Agent HTTP header, use `auto` for coyote/<current-version>
|
||||
save_shell_history: true # Whether to save shell execution command to the history file
|
||||
sync_models_url: > # URL to sync model changes from
|
||||
https://raw.githubusercontent.com/Dark-Alex-17/loki/refs/heads/main/models.yaml
|
||||
https://raw.githubusercontent.com/Dark-Alex-17/coyote/refs/heads/main/models.yaml
|
||||
|
||||
# ---- REPL Prompt ----
|
||||
# Custom REPL left/right prompts; see the [REPL Prompt Documentation](https://github.com/Dark-Alex-17/loki/wiki/REPL-Prompt) for more information
|
||||
# Custom REPL left/right prompts; see the [REPL Prompt Documentation](https://github.com/Dark-Alex-17/coyote/wiki/REPL-Prompt) for more information
|
||||
left_prompt:
|
||||
'{color.red}{model}){color.green}{?session {?agent {agent}>}{session}{?role /}}{!session {?agent {agent}>}}{role}{?rag @{rag}}{color.cyan}{?session )}{!session >}{color.reset} '
|
||||
right_prompt:
|
||||
'{color.purple}{?session {?consume_tokens {consume_tokens}({consume_percent}%)}{!consume_tokens {consume_tokens}}}{color.reset}'
|
||||
|
||||
# ---- Vault ----
|
||||
# See the [Vault documentation](https://github.com/Dark-Alex-17/loki/wiki/Vault) for more information on the Loki vault
|
||||
vault_password_file: null # Path to a file containing the password for the Loki vault (cannot be a secret template)
|
||||
# See the [Vault documentation](https://github.com/Dark-Alex-17/coyote/wiki/Vault) for more information on the Coyote vault
|
||||
vault_password_file: null # Path to a file containing the password for the Coyote vault (cannot be a secret template)
|
||||
|
||||
# ---- Function Calling ----
|
||||
# See the [Tools documentation](https://github.com/Dark-Alex-17/loki/wiki/Tools) for more details
|
||||
# See the [Tools documentation](https://github.com/Dark-Alex-17/coyote/wiki/Tools) for more details
|
||||
function_calling: true # Enables or disables function calling (Globally).
|
||||
mapping_tools: # Alias for a tool or toolset
|
||||
fs: 'fs_cat,fs_ls,fs_mkdir,fs_rm,fs_write,fs_read,fs_glob,fs_grep'
|
||||
enabled_tools: null # Which tools to enable by default. (e.g. 'fs,web_search_loki')
|
||||
enabled_tools: null # Which tools to enable by default. (e.g. 'fs,web_search_coyote')
|
||||
visible_tools: # Which tools are visible to be compiled (and are thus able to be defined in 'enabled_tools')
|
||||
# - demo_py.py
|
||||
# - demo_sh.sh
|
||||
@@ -69,12 +69,12 @@ visible_tools: # Which tools are visible to be compiled (and a
|
||||
# - search_wolframalpha.sh
|
||||
# - send_mail.sh
|
||||
# - send_twilio.sh
|
||||
# - web_search_loki.sh
|
||||
# - web_search_coyote.sh
|
||||
# - web_search_perplexity.sh
|
||||
# - web_search_tavily.sh
|
||||
|
||||
# ---- MCP Servers ----
|
||||
# See the [MCP Servers documentation](https://github.com/Dark-Alex-17/loki/wiki/MCP-Servers) for more details
|
||||
# See the [MCP Servers documentation](https://github.com/Dark-Alex-17/coyote/wiki/MCP-Servers) for more details
|
||||
mcp_server_support: true # Enables or disables MCP servers (globally).
|
||||
mapping_mcp_servers: # Alias for an MCP server or set of servers
|
||||
git: github,gitmcp
|
||||
@@ -84,14 +84,14 @@ enabled_mcp_servers: null # Which MCP servers to enable by default (e.g.
|
||||
# The auto-continue system provides built-in task tracking for improved reliability.
|
||||
# When enabled, the model can create todo lists and the system will automatically
|
||||
# prompt it to continue when incomplete tasks remain.
|
||||
# See the [Todo System documentation](https://github.com/Dark-Alex-17/loki/wiki/TODO-System) for more information
|
||||
# See the [Todo System documentation](https://github.com/Dark-Alex-17/coyote/wiki/TODO-System) for more information
|
||||
auto_continue: false # Enable automatic continuation when incomplete todos remain (default: false)
|
||||
max_auto_continues: 10 # Maximum number of automatic continuations before stopping (default: 10)
|
||||
inject_todo_instructions: true # Inject default todo usage instructions into the system prompt (default: true)
|
||||
continuation_prompt: null # Custom prompt used when auto-continuing. If null, uses built-in default
|
||||
|
||||
# ---- Session ----
|
||||
# See the [Session documentation](https://github.com/Dark-Alex-17/loki/wiki/Sessions) for more information
|
||||
# See the [Session documentation](https://github.com/Dark-Alex-17/coyote/wiki/Sessions) for more information
|
||||
save_session: null # Controls the persistence of the session. If true, auto save; if false, don't auto-save save; if null, ask the user what to do
|
||||
compression_threshold: 4000 # Compress the session when the token count reaches or exceeds this threshold
|
||||
summarization_prompt: > # The text prompt used for creating a concise summary of session message
|
||||
@@ -100,9 +100,9 @@ summary_context_prompt: > # The text prompt used for including the summar
|
||||
'This is a summary of the chat history as a recap: '
|
||||
|
||||
# ---- RAG ----
|
||||
# See the [RAG Docs](https://github.com/Dark-Alex-17/loki/wiki/RAG) for more details.
|
||||
# See the [RAG Docs](https://github.com/Dark-Alex-17/coyote/wiki/RAG) for more details.
|
||||
rag_embedding_model: null # Specifies the embedding model used for context retrieval
|
||||
rag_reranker_model: null # Specifies the reranker model used for sorting retrieved documents; Loki uses Reciprocal Rank Fusion by default
|
||||
rag_reranker_model: null # Specifies the reranker model used for sorting retrieved documents; Coyote uses Reciprocal Rank Fusion by default
|
||||
rag_top_k: 5 # Specifies the number of documents to retrieve for answering queries
|
||||
rag_chunk_size: null # Defines the size of chunks for document processing in characters
|
||||
rag_chunk_overlap: null # Defines the overlap between chunks
|
||||
@@ -141,12 +141,12 @@ document_loaders:
|
||||
docx: 'pandoc --to plain $1' # Use pandoc to convert a .docx file to text
|
||||
# (see https://pandoc.org for details on how to install pandoc)
|
||||
jina: 'curl -fsSL https://r.jina.ai/$1 -H "Authorization: Bearer {{JINA_API_KEY}}' # Use Jina to translate a website into text;
|
||||
# Requires a Jina API key to be added to the Loki vault
|
||||
# Requires a Jina API key to be added to the Coyote vault
|
||||
git: > # Use yek to load a git repository into the knowledgebase (https://github.com/bodo-run/yek)
|
||||
sh -c "yek $1 --json | jq 'map({ path: .filename, contents: .content })'"
|
||||
|
||||
# ---- Clients ----
|
||||
# See the [Clients documentation](https://github.com/Dark-Alex-17/loki/wiki/Clients) for more details
|
||||
# See the [Clients documentation](https://github.com/Dark-Alex-17/coyote/wiki/Clients) for more details
|
||||
clients:
|
||||
# All clients have the following configuration:
|
||||
# - type: xxxx
|
||||
@@ -177,14 +177,14 @@ clients:
|
||||
# See https://platform.openai.com/docs/quickstart
|
||||
- type: openai
|
||||
api_base: https://api.openai.com/v1 # Optional
|
||||
api_key: '{{OPENAI_API_KEY}}' # You can either hard-code or inject secrets from the Loki vault
|
||||
api_key: '{{OPENAI_API_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
||||
organization_id: org-xxx # Optional
|
||||
|
||||
# For any platform compatible with OpenAI's API
|
||||
- type: openai-compatible
|
||||
name: ollama
|
||||
api_base: http://localhost:11434/v1
|
||||
api_key: '{{OLLAMA_API_KEY}}' # Optional; You can either hard-code or inject secrets from the Loki vault
|
||||
api_key: '{{OLLAMA_API_KEY}}' # Optional; You can either hard-code or inject secrets from the Coyote vault
|
||||
models:
|
||||
- name: deepseek-r1
|
||||
max_input_tokens: 131072
|
||||
@@ -202,9 +202,9 @@ clients:
|
||||
# See https://ai.google.dev/docs
|
||||
- type: gemini
|
||||
api_base: https://generativelanguage.googleapis.com/v1beta
|
||||
api_key: '{{GEMINI_API_KEY}}' # You can either hard-code or inject secrets from the Loki vault
|
||||
auth: null # When set to 'oauth', Loki will use OAuth instead of an API key
|
||||
# Authenticate with `loki --authenticate` or `.authenticate` in the REPL
|
||||
api_key: '{{GEMINI_API_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
||||
auth: null # When set to 'oauth', Coyote will use OAuth instead of an API key
|
||||
# Authenticate with `coyote --authenticate` or `.authenticate` in the REPL
|
||||
patch:
|
||||
chat_completions:
|
||||
'.*':
|
||||
@@ -222,49 +222,49 @@ clients:
|
||||
# See https://docs.anthropic.com/claude/reference/getting-started-with-the-api
|
||||
- type: claude
|
||||
api_base: https://api.anthropic.com/v1 # Optional
|
||||
api_key: '{{ANTHROPIC_API_KEY}}' # You can either hard-code or inject secrets from the Loki vault
|
||||
auth: null # When set to 'oauth', Loki will use OAuth instead of an API key
|
||||
# Authenticate with `loki --authenticate` or `.authenticate` in the REPL
|
||||
api_key: '{{ANTHROPIC_API_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
||||
auth: null # When set to 'oauth', Coyote will use OAuth instead of an API key
|
||||
# Authenticate with `coyote --authenticate` or `.authenticate` in the REPL
|
||||
|
||||
# See https://docs.mistral.ai/
|
||||
- type: openai-compatible
|
||||
name: mistral
|
||||
api_base: https://api.mistral.ai/v1
|
||||
api_key: '{{MISTRAL_API_KEY}}' # You can either hard-code or inject secrets from the Loki vault
|
||||
api_key: '{{MISTRAL_API_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
||||
|
||||
# See https://docs.x.ai/docs
|
||||
- type: openai-compatible
|
||||
name: xai
|
||||
api_base: https://api.x.ai/v1
|
||||
api_key: '{{XAI_API_KEY}}' # You can either hard-code or inject secrets from the Loki vault
|
||||
api_key: '{{XAI_API_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
||||
|
||||
# See https://docs.ai21.com/docs/overview
|
||||
- type: openai-compatible
|
||||
name: ai12
|
||||
api_base: https://api.ai21.com/studio/v1
|
||||
api_key: '{{AI21_API_KEY}}' # You can either hard-code or inject secrets from the Loki vault
|
||||
api_key: '{{AI21_API_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
||||
|
||||
# See https://docs.cohere.com/docs/the-cohere-platform
|
||||
- type: cohere
|
||||
api_base: https://api.cohere.ai/v2 # Optional
|
||||
api_key: '{{COHERE_API_KEY}}' # You can either hard-code or inject secrets from the Loki vault
|
||||
api_key: '{{COHERE_API_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
||||
|
||||
# See https://docs.perplexity.ai/getting-started/overview
|
||||
- type: openai-compatible
|
||||
name: perplexity
|
||||
api_base: https://api.perplexity.ai
|
||||
api_key: '{{PERPLEXITY_API_KEY}}' # You can either hard-code or inject secrets from the Loki vault
|
||||
api_key: '{{PERPLEXITY_API_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
||||
|
||||
# See https://console.groq.com/docs/quickstart
|
||||
- type: openai-compatible
|
||||
name: groq
|
||||
api_base: https://api.groq.com/openai/v1
|
||||
api_key: '{{GROQ_API_KEY}}' # You can either hard-code or inject secrets from the Loki vault
|
||||
api_key: '{{GROQ_API_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
||||
|
||||
# See https://learn.microsoft.com/en-us/azure/ai-services/openai/chatgpt-quickstart
|
||||
- type: azure-openai
|
||||
api_base: https://{RESOURCE}.openai.azure.com
|
||||
api_key: '{{AZURE_OPENAI_API_KEY}}' # You can either hard-code or inject secrets from the Loki vault
|
||||
api_key: '{{AZURE_OPENAI_API_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
||||
models:
|
||||
- name: gpt-4o # Model deployment name
|
||||
max_input_tokens: 128000
|
||||
@@ -295,8 +295,8 @@ clients:
|
||||
|
||||
# See https://docs.aws.amazon.com/bedrock/latest/userguide/
|
||||
- type: bedrock
|
||||
access_key_id: '{{AWS_ACCESS_KEY_ID}}' # You can either hard-code or inject secrets from the Loki vault
|
||||
secret_access_key: '{{AWS_SECRET_ACCESS_KEY}}' # You can either hard-code or inject secrets from the Loki vault
|
||||
access_key_id: '{{AWS_ACCESS_KEY_ID}}' # You can either hard-code or inject secrets from the Coyote vault
|
||||
secret_access_key: '{{AWS_SECRET_ACCESS_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
||||
region: xxx
|
||||
session_token: xxx # Optional, only needed for temporary credentials
|
||||
|
||||
@@ -304,67 +304,67 @@ clients:
|
||||
- type: openai-compatible
|
||||
name: cloudflare
|
||||
api_base: https://api.cloudflare.com/client/v4/accounts/{ACCOUNT_ID}/ai/v1
|
||||
api_key: '{{CLOUDFLARE_API_KEY}}' # You can either hard-code or inject secrets from the Loki vault
|
||||
api_key: '{{CLOUDFLARE_API_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
||||
|
||||
# See https://cloud.baidu.com/doc/WENXINWORKSHOP/index.html
|
||||
- type: openai-compatible
|
||||
name: ernie
|
||||
api_base: https://qianfan.baidubce.com/v2
|
||||
api_key: '{{BAIDU_API_KEY}}' # You can either hard-code or inject secrets from the Loki vault
|
||||
api_key: '{{BAIDU_API_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
||||
|
||||
# See https://dashscope.aliyun.com/
|
||||
- type: openai-compatible
|
||||
name: qianwen
|
||||
api_base: https://dashscope.aliyuncs.com/compatible-mode/v1
|
||||
api_key: '{{ALIYUN_API_KEY}}' # You can either hard-code or inject secrets from the Loki vault
|
||||
api_key: '{{ALIYUN_API_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
||||
|
||||
# See https://cloud.tencent.com/product/hunyuan
|
||||
- type: openai-compatible
|
||||
name: hunyuan
|
||||
api_base: https://api.hunyuan.cloud.tencent.com/v1
|
||||
api_key: '{{TENCENT_API_KEY}}' # You can either hard-code or inject secrets from the Loki vault
|
||||
api_key: '{{TENCENT_API_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
||||
|
||||
# See https://platform.moonshot.cn/docs/intro
|
||||
- type: openai-compatible
|
||||
name: moonshot
|
||||
api_base: https://api.moonshot.cn/v1
|
||||
api_key: '{{MOONSHOT_API_KEY}}' # You can either hard-code or inject secrets from the Loki vault
|
||||
api_key: '{{MOONSHOT_API_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
||||
|
||||
# See https://platform.deepseek.com/api-docs/
|
||||
- type: openai-compatible
|
||||
name: deepseek
|
||||
api_base: https://api.deepseek.com
|
||||
api_key: '{{DEEPSEEK_API_KEY}}' # You can either hard-code or inject secrets from the Loki vault
|
||||
api_key: '{{DEEPSEEK_API_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
||||
|
||||
# See https://open.bigmodel.cn/dev/howuse/introduction
|
||||
- type: openai-compatible
|
||||
name: zhipuai
|
||||
api_base: https://open.bigmodel.cn/api/paas/v4
|
||||
api_key: '{{ZHIPUAI_API_KEY}}' # You can either hard-code or inject secrets from the Loki vault
|
||||
api_key: '{{ZHIPUAI_API_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
||||
|
||||
# See https://platform.minimaxi.com/document/Fast%20access
|
||||
- type: openai-compatible
|
||||
name: minimax
|
||||
api_base: https://api.minimax.chat/v1
|
||||
api_key: '{{MINIMAX_API_KEY}}' # You can either hard-code or inject secrets from the Loki vault
|
||||
api_key: '{{MINIMAX_API_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
||||
|
||||
# See https://openrouter.ai/docs#quick-start
|
||||
- type: openai-compatible
|
||||
name: openrouter
|
||||
api_base: https://openrouter.ai/api/v1
|
||||
api_key: '{{OPENROUTER_API_KEY}}' # You can either hard-code or inject secrets from the Loki vault
|
||||
api_key: '{{OPENROUTER_API_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
||||
|
||||
# See https://github.com/marketplace/models
|
||||
- type: openai-compatible
|
||||
name: github
|
||||
api_base: https://models.inference.ai.azure.com
|
||||
api_key: '{{GITHUB_API_KEY}}' # You can either hard-code or inject secrets from the Loki vault
|
||||
api_key: '{{GITHUB_API_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
||||
|
||||
# See https://deepinfra.com/docs
|
||||
- type: openai-compatible
|
||||
name: deepinfra
|
||||
api_base: https://api.deepinfra.com/v1/openai
|
||||
api_key: '{{DEEPINFRA_API_KEY}}' # You can either hard-code or inject secrets from the Loki vault
|
||||
api_key: '{{DEEPINFRA_API_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
||||
|
||||
|
||||
# ----- RAG dedicated -----
|
||||
@@ -373,10 +373,10 @@ clients:
|
||||
- type: openai-compatible
|
||||
name: jina
|
||||
api_base: https://api.jina.ai/v1
|
||||
api_key: '{{JINA_API_KEY}}' # You can either hard-code or inject secrets from the Loki vault
|
||||
api_key: '{{JINA_API_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
||||
|
||||
# See https://docs.voyageai.com/docs/introduction
|
||||
- type: openai-compatible
|
||||
name: voyageai
|
||||
api_base: https://api.voyageai.com/v1
|
||||
api_key: '{{VOYAGEAI_API_KEY}}' # You can either hard-code or inject secrets from the Loki vault
|
||||
api_key: '{{VOYAGEAI_API_KEY}}' # You can either hard-code or inject secrets from the Coyote vault
|
||||
|
||||
@@ -16,7 +16,7 @@ prompt: null # A custom prompt to use for this role tha
|
||||
# The auto-continue system provides built-in task tracking for improved reliability.
|
||||
# When enabled, the model can create todo lists and the system will automatically
|
||||
# prompt it to continue when incomplete tasks remain.
|
||||
# See the [Todo System documentation](https://github.com/Dark-Alex-17/loki/wiki/TODO-System) for more information
|
||||
# See the [Todo System documentation](https://github.com/Dark-Alex-17/coyote/wiki/TODO-System) for more information
|
||||
auto_continue: false # Enable automatic continuation when incomplete todos remain (default: false)
|
||||
max_auto_continues: 10 # Maximum number of automatic continuations before stopping (default: 10)
|
||||
inject_todo_instructions: true # Inject default todo tool usage instructions into the system prompt (default: true)
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
# Documentation: https://docs.brew.sh/Formula-Cookbook
|
||||
# https://rubydoc.brew.sh/Formula
|
||||
class Coyote < Formula
|
||||
desc "All-in-one, batteries included LLM CLI tool"
|
||||
homepage "https://github.com/Dark-Alex-17/coyote"
|
||||
if OS.mac? and Hardware::CPU.arm?
|
||||
url "https://github.com/Dark-Alex-17/coyote/releases/download/v$version/coyote-aarch64-apple-darwin.tar.gz"
|
||||
sha256 "$hash_mac_arm"
|
||||
elsif OS.mac? and Hardware::CPU.intel?
|
||||
url "https://github.com/Dark-Alex-17/coyote/releases/download/v$version/coyote-x86_64-apple-darwin.tar.gz"
|
||||
sha256 "$hash_mac"
|
||||
else
|
||||
url "https://github.com/Dark-Alex-17/coyote/releases/download/v$version/coyote-x86_64-unknown-linux-musl.tar.gz"
|
||||
sha256 "$hash_linux"
|
||||
end
|
||||
version "$version"
|
||||
license "MIT"
|
||||
|
||||
def install
|
||||
bin.install "coyote"
|
||||
ohai "You're done! Get started with \"coyote --help\""
|
||||
end
|
||||
end
|
||||
@@ -1,23 +0,0 @@
|
||||
# Documentation: https://docs.brew.sh/Formula-Cookbook
|
||||
# https://rubydoc.brew.sh/Formula
|
||||
class Loki < Formula
|
||||
desc "All-in-one, batteries included LLM CLI tool"
|
||||
homepage "https://github.com/Dark-Alex-17/loki"
|
||||
if OS.mac? and Hardware::CPU.arm?
|
||||
url "https://github.com/Dark-Alex-17/loki/releases/download/v$version/loki-aarch64-apple-darwin.tar.gz"
|
||||
sha256 "$hash_mac_arm"
|
||||
elsif OS.mac? and Hardware::CPU.intel?
|
||||
url "https://github.com/Dark-Alex-17/loki/releases/download/v$version/loki-x86_64-apple-darwin.tar.gz"
|
||||
sha256 "$hash_mac"
|
||||
else
|
||||
url "https://github.com/Dark-Alex-17/loki/releases/download/v$version/loki-x86_64-unknown-linux-musl.tar.gz"
|
||||
sha256 "$hash_linux"
|
||||
end
|
||||
version "$version"
|
||||
license "MIT"
|
||||
|
||||
def install
|
||||
bin.install "loki"
|
||||
ohai "You're done! Get started with \"loki --help\""
|
||||
end
|
||||
end
|
||||
+14
-14
@@ -1,5 +1,5 @@
|
||||
# Graph-based agent definition (full-featured reference)
|
||||
# Location: <loki-config-dir>/agents/<agent-name>/graph.yaml
|
||||
# Location: <coyote-config-dir>/agents/<agent-name>/graph.yaml
|
||||
#
|
||||
# A graph agent is defined by this file alone. An agent directory contains
|
||||
# either a config.yaml (a normal LLM-loop agent) or a graph.yaml (a graph
|
||||
@@ -13,7 +13,7 @@
|
||||
# runnable deep-research graph agent, see assets/agents/deep-research/.
|
||||
#
|
||||
# Full documentation:
|
||||
# https://github.com/Dark-Alex-17/loki/wiki/Graph-Agents
|
||||
# https://github.com/Dark-Alex-17/coyote/wiki/Graph-Agents
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Identity
|
||||
@@ -35,7 +35,7 @@ temperature: 0.0 # Default sampling temperature for `llm` node
|
||||
top_p: null # Default sampling top-p for `llm` nodes
|
||||
|
||||
global_tools: # Tool universe an `llm` node's `tools:` whitelist draws from
|
||||
- web_search_loki.sh
|
||||
- web_search_coyote.sh
|
||||
- fetch_url_via_curl.sh
|
||||
|
||||
mcp_servers: # MCP servers an `llm` node may reference via `mcp:<server>`
|
||||
@@ -52,7 +52,7 @@ conversation_starters: # Suggested prompts surfaced in the UI
|
||||
# (see initial_state below).
|
||||
# - Script nodes via the env var `LLM_AGENT_VAR_<UPPER_NAME>`.
|
||||
# Values may be overridden at runtime with
|
||||
# `loki -a <agent> --agent-variable <name> <value> "..."`.
|
||||
# `coyote -a <agent> --agent-variable <name> <value> "..."`.
|
||||
# ---------------------------------------------------------------------------
|
||||
variables:
|
||||
- name: project_dir
|
||||
@@ -103,7 +103,7 @@ reducers:
|
||||
# Values placed into graph state before any node runs; reference anywhere via
|
||||
# {{key}}.
|
||||
#
|
||||
# Note: `initial_prompt` is seeded automatically by Loki with the
|
||||
# Note: `initial_prompt` is seeded automatically by Coyote with the
|
||||
# caller's prompt. So there's no need to set it here.
|
||||
# ---------------------------------------------------------------------------
|
||||
initial_state:
|
||||
@@ -123,7 +123,7 @@ start: triage # ID of the first node to run (must exist in `nodes
|
||||
# ---------------------------------------------------------------------------
|
||||
# Nodes
|
||||
# Each node is keyed by its id. The `id:` inside a node must match its key
|
||||
# (it may also be omitted and thus Loki fills it in from the key).
|
||||
# (it may also be omitted and thus Coyote fills it in from the key).
|
||||
#
|
||||
# Node types: agent | script | approval | input | llm | rag | map | end
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -202,7 +202,7 @@ nodes:
|
||||
instructions: "You are a web researcher. Cite every claim."
|
||||
prompt: "Web research: {{topic}}. Return findings and sources."
|
||||
tools:
|
||||
- web_search_loki
|
||||
- web_search_coyote
|
||||
- mcp:ddg-search
|
||||
output_schema:
|
||||
type: object
|
||||
@@ -226,13 +226,13 @@ nodes:
|
||||
# The script also receives these env vars (parity with bash tools called
|
||||
# from normal agents):
|
||||
# GRAPH_STATE / GRAPH_STATE_FILE state payload (one of the two is set)
|
||||
# LLM_ROOT_DIR loki config dir
|
||||
# LLM_ROOT_DIR coyote config dir
|
||||
# LLM_PROMPT_UTILS_FILE path to .shared/prompt-utils.sh
|
||||
# LLM_AGENT_DATA_DIR this agent's data directory
|
||||
# LLM_AGENT_VAR_<NAME> one per declared `variables:` entry
|
||||
# PATH with loki's functions bin dir prepended
|
||||
# PATH with coyote's functions bin dir prepended
|
||||
# CLICOLOR_FORCE / FORCE_COLOR so child tools emit ANSI colors
|
||||
# The script's working directory is loki's invocation CWD (not the agent
|
||||
# The script's working directory is coyote's invocation CWD (not the agent
|
||||
# directory), matching the behavior of bash tools.
|
||||
#
|
||||
# This node fires once: after both `retrieve` and `web_search` finish.
|
||||
@@ -256,13 +256,13 @@ nodes:
|
||||
# targets.
|
||||
|
||||
# --- agent node ---------------------------------------------------------
|
||||
# Spawns a full Loki sub-agent and waits for it. The child uses its own
|
||||
# Spawns a full Coyote sub-agent and waits for it. The child uses its own
|
||||
# tool stack. Agent nodes have no `tools:` field. No schema hint is
|
||||
# injected even when `output_schema` is set (unlike llm nodes).
|
||||
deep_dive:
|
||||
id: deep_dive
|
||||
type: agent
|
||||
agent: deep-research # Name of an existing Loki agent to spawn
|
||||
agent: deep-research # Name of an existing Coyote agent to spawn
|
||||
prompt: | # User message sent to the child (templated)
|
||||
Research {{topic}} in depth. Existing context:
|
||||
{{context}}
|
||||
@@ -325,7 +325,7 @@ nodes:
|
||||
instructions: "Research one subject deeply for a {{audience}} audience."
|
||||
prompt: "Research {{subject}}: pull the key facts and one citation."
|
||||
tools:
|
||||
- web_search_loki
|
||||
- web_search_coyote
|
||||
# No `next:`, `state_updates:`, or `output_schema:` here. Map branches
|
||||
# have a strict contract (see `subjects_map.branch` comment).
|
||||
|
||||
@@ -348,7 +348,7 @@ nodes:
|
||||
instructions: "You write concise research summaries for a {{audience}} audience."
|
||||
prompt: "Summarize the topic {{topic}}, using your tools as needed."
|
||||
tools: # Narrow whitelist: exactly these entries, nothing else
|
||||
- web_search_loki # an exact global-tool / custom-tool name
|
||||
- web_search_coyote # an exact global-tool / custom-tool name
|
||||
- mcp:ddg-search # `mcp:<server>` includes that server's functions
|
||||
model: claude:claude-haiku-4-5 # Optional per-node model override
|
||||
temperature: 0.3 # Optional per-node sampling override
|
||||
|
||||
@@ -18,7 +18,7 @@ fmt:
|
||||
cargo fmt --all
|
||||
|
||||
# Build the project for the current system architecture
|
||||
# (Gets stored at ./target/[debug|release]/loki)
|
||||
# (Gets stored at ./target/[debug|release]/coyote)
|
||||
[group: 'build']
|
||||
[arg('build_type', pattern="debug|release")]
|
||||
build build_type='debug':
|
||||
|
||||
Generated
+1
-1
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "loki",
|
||||
"name": "coyote",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {}
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
<#
|
||||
loki installer (Windows/PowerShell 5+ and PowerShell 7)
|
||||
coyote installer (Windows/PowerShell 5+ and PowerShell 7)
|
||||
|
||||
Examples:
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -Command "iwr -useb https://raw.githubusercontent.com/Dark-Alex-17/loki/main/scripts/install_loki.ps1 | iex"
|
||||
pwsh -c "irm https://raw.githubusercontent.com/Dark-Alex-17/loki/main/scripts/install_loki.ps1 | iex -Version vX.Y.Z"
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -Command "iwr -useb https://raw.githubusercontent.com/Dark-Alex-17/coyote/main/scripts/install_coyote.ps1 | iex"
|
||||
pwsh -c "irm https://raw.githubusercontent.com/Dark-Alex-17/coyote/main/scripts/install_coyote.ps1 | iex -Version vX.Y.Z"
|
||||
|
||||
Parameters:
|
||||
-Version <tag> (default: latest)
|
||||
-BinDir <path> (default: %LOCALAPPDATA%\loki\bin on Windows; ~/.local/bin on *nix PowerShell)
|
||||
-BinDir <path> (default: %LOCALAPPDATA%\coyote\bin on Windows; ~/.local/bin on *nix PowerShell)
|
||||
#>
|
||||
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[string]$Version = $env:LOKI_VERSION,
|
||||
[string]$Version = $env:COYOTE_VERSION,
|
||||
[string]$BinDir = $env:BIN_DIR
|
||||
)
|
||||
|
||||
$Repo = 'Dark-Alex-17/loki'
|
||||
$Repo = 'Dark-Alex-17/coyote'
|
||||
|
||||
function Write-Info($msg) { Write-Host "[loki-install] $msg" }
|
||||
function Write-Info($msg) { Write-Host "[coyote-install] $msg" }
|
||||
function Fail($msg) { Write-Error $msg; exit 1 }
|
||||
|
||||
Add-Type -AssemblyName System.Runtime
|
||||
@@ -38,7 +38,7 @@ switch ([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture) {
|
||||
}
|
||||
|
||||
if (-not $BinDir) {
|
||||
if ($isWin) { $BinDir = Join-Path $env:LOCALAPPDATA 'loki\bin' }
|
||||
if ($isWin) { $BinDir = Join-Path $env:LOCALAPPDATA 'coyote\bin' }
|
||||
else { $home = $env:HOME; if (-not $home) { $home = (Get-Item -Path ~).FullName }; $BinDir = Join-Path $home '.local/bin' }
|
||||
}
|
||||
New-Item -ItemType Directory -Force -Path $BinDir | Out-Null
|
||||
@@ -49,23 +49,23 @@ $apiBase = "https://api.github.com/repos/$Repo/releases"
|
||||
$relUrl = if ($Version) { "$apiBase/tags/$Version" } else { "$apiBase/latest" }
|
||||
Write-Info "Fetching release: $relUrl"
|
||||
try {
|
||||
$release = Invoke-RestMethod -UseBasicParsing -Headers @{ 'User-Agent' = 'loki-installer' } -Uri $relUrl -Method GET
|
||||
$release = Invoke-RestMethod -UseBasicParsing -Headers @{ 'User-Agent' = 'coyote-installer' } -Uri $relUrl -Method GET
|
||||
} catch { Fail "Failed to fetch release metadata. $_" }
|
||||
if (-not $release.assets) { Fail "No assets found in the release." }
|
||||
|
||||
$candidates = @()
|
||||
if ($os -eq 'windows') {
|
||||
if ($arch -eq 'x86_64') { $candidates += 'loki-x86_64-pc-windows-msvc.zip' }
|
||||
else { $candidates += 'loki-aarch64-pc-windows-msvc.zip' }
|
||||
if ($arch -eq 'x86_64') { $candidates += 'coyote-x86_64-pc-windows-msvc.zip' }
|
||||
else { $candidates += 'coyote-aarch64-pc-windows-msvc.zip' }
|
||||
} elseif ($os -eq 'darwin') {
|
||||
if ($arch -eq 'x86_64') { $candidates += 'loki-x86_64-apple-darwin.tar.gz' }
|
||||
else { $candidates += 'loki-aarch64-apple-darwin.tar.gz' }
|
||||
if ($arch -eq 'x86_64') { $candidates += 'coyote-x86_64-apple-darwin.tar.gz' }
|
||||
else { $candidates += 'coyote-aarch64-apple-darwin.tar.gz' }
|
||||
} elseif ($os -eq 'linux') {
|
||||
if ($arch -eq 'x86_64') {
|
||||
$candidates += 'loki-x86_64-unknown-linux-gnu.tar.gz'
|
||||
$candidates += 'loki-x86_64-unknown-linux-musl.tar.gz'
|
||||
$candidates += 'coyote-x86_64-unknown-linux-gnu.tar.gz'
|
||||
$candidates += 'coyote-x86_64-unknown-linux-musl.tar.gz'
|
||||
} else {
|
||||
$candidates += 'loki-aarch64-unknown-linux-musl.tar.gz'
|
||||
$candidates += 'coyote-aarch64-unknown-linux-musl.tar.gz'
|
||||
}
|
||||
} else {
|
||||
Fail "Unsupported OS for this installer: $os"
|
||||
@@ -84,9 +84,9 @@ if (-not $asset) {
|
||||
Write-Info "Selected asset: $($asset.name)"
|
||||
Write-Info "Download URL: $($asset.browser_download_url)"
|
||||
|
||||
$tmp = New-Item -ItemType Directory -Force -Path ([IO.Path]::Combine([IO.Path]::GetTempPath(), "loki-$(Get-Random)"))
|
||||
$tmp = New-Item -ItemType Directory -Force -Path ([IO.Path]::Combine([IO.Path]::GetTempPath(), "coyote-$(Get-Random)"))
|
||||
$archive = Join-Path $tmp.FullName 'asset'
|
||||
try { Invoke-WebRequest -UseBasicParsing -Headers @{ 'User-Agent' = 'loki-installer' } -Uri $asset.browser_download_url -OutFile $archive } catch { Fail "Failed to download asset. $_" }
|
||||
try { Invoke-WebRequest -UseBasicParsing -Headers @{ 'User-Agent' = 'coyote-installer' } -Uri $asset.browser_download_url -OutFile $archive } catch { Fail "Failed to download asset. $_" }
|
||||
|
||||
$extractDir = Join-Path $tmp.FullName 'extract'; New-Item -ItemType Directory -Force -Path $extractDir | Out-Null
|
||||
|
||||
@@ -107,14 +107,14 @@ if ($asset.name -match '\.zip$') {
|
||||
|
||||
$bin = $null
|
||||
Get-ChildItem -Recurse -File $extractDir | ForEach-Object {
|
||||
if ($isWin) { if ($_.Name -ieq 'loki.exe') { $bin = $_.FullName } }
|
||||
else { if ($_.Name -ieq 'loki') { $bin = $_.FullName } }
|
||||
if ($isWin) { if ($_.Name -ieq 'coyote.exe') { $bin = $_.FullName } }
|
||||
else { if ($_.Name -ieq 'coyote') { $bin = $_.FullName } }
|
||||
}
|
||||
if (-not $bin) { Fail "Could not find loki binary inside the archive." }
|
||||
if (-not $bin) { Fail "Could not find coyote binary inside the archive." }
|
||||
|
||||
if (-not $isWin) { try { & chmod +x -- $bin } catch {} }
|
||||
|
||||
$exec = if ($isWin) { 'loki.exe'} else { 'loki' }
|
||||
$exec = if ($isWin) { 'coyote.exe'} else { 'coyote' }
|
||||
$dest = Join-Path $BinDir $exec
|
||||
Copy-Item -Force $bin $dest
|
||||
Write-Info "Installed: $dest"
|
||||
@@ -135,5 +135,5 @@ if ($isWin) {
|
||||
}
|
||||
}
|
||||
|
||||
Write-Info "Done. Try: loki --help"
|
||||
Write-Info "Done. Try: coyote --help"
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# loki installer (Linux/macOS)
|
||||
# coyote installer (Linux/macOS)
|
||||
#
|
||||
# Usage examples:
|
||||
# curl -fsSL https://raw.githubusercontent.com/Dark-Alex-17/loki/main/scripts/install_loki.sh | bash
|
||||
# curl -fsSL https://raw.githubusercontent.com/Dark-Alex-17/loki/main/scripts/install_loki.sh | bash -s -- --version vX.Y.Z
|
||||
# BIN_DIR="$HOME/.local/bin" bash scripts/install_loki.sh
|
||||
# curl -fsSL https://raw.githubusercontent.com/Dark-Alex-17/coyote/main/scripts/install_coyote.sh | bash
|
||||
# curl -fsSL https://raw.githubusercontent.com/Dark-Alex-17/coyote/main/scripts/install_coyote.sh | bash -s -- --version vX.Y.Z
|
||||
# BIN_DIR="$HOME/.local/bin" bash scripts/install_coyote.sh
|
||||
#
|
||||
# Flags / Env:
|
||||
# --version <tag> Release tag (default: latest). Or set LOKI_VERSION.
|
||||
# --version <tag> Release tag (default: latest). Or set COYOTE_VERSION.
|
||||
# --bin-dir <dir> Install directory (default: /usr/local/bin or ~/.local/bin). Or set BIN_DIR.
|
||||
|
||||
REPO="Dark-Alex-17/loki"
|
||||
VERSION="${LOKI_VERSION:-}"
|
||||
REPO="Dark-Alex-17/coyote"
|
||||
VERSION="${COYOTE_VERSION:-}"
|
||||
BIN_DIR="${BIN_DIR:-}"
|
||||
|
||||
usage() {
|
||||
echo "loki installer (Linux/macOS)"
|
||||
echo "coyote installer (Linux/macOS)"
|
||||
echo
|
||||
echo "Options:"
|
||||
echo " --version <tag> Release tag (default: latest)"
|
||||
@@ -44,7 +44,7 @@ fi
|
||||
mkdir -p "${BIN_DIR}"
|
||||
|
||||
log() {
|
||||
echo "[loki-install] $*"
|
||||
echo "[coyote-install] $*"
|
||||
}
|
||||
|
||||
need_cmd() {
|
||||
@@ -92,9 +92,9 @@ fi
|
||||
|
||||
http_get() {
|
||||
if [[ "$DL" == "curl" ]]; then
|
||||
curl -fsSL -H 'User-Agent: loki-installer' "$1"
|
||||
curl -fsSL -H 'User-Agent: coyote-installer' "$1"
|
||||
else
|
||||
wget -qO- --header='User-Agent: loki-installer' "$1"
|
||||
wget -qO- --header='User-Agent: coyote-installer' "$1"
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -111,9 +111,9 @@ fi
|
||||
ASSET_CANDIDATES=()
|
||||
if [[ "$OS" == "darwin" ]]; then
|
||||
if [[ "$ARCH" == "x86_64" ]]; then
|
||||
ASSET_CANDIDATES+=("loki-x86_64-apple-darwin.tar.gz")
|
||||
ASSET_CANDIDATES+=("coyote-x86_64-apple-darwin.tar.gz")
|
||||
else
|
||||
ASSET_CANDIDATES+=("loki-aarch64-apple-darwin.tar.gz")
|
||||
ASSET_CANDIDATES+=("coyote-aarch64-apple-darwin.tar.gz")
|
||||
fi
|
||||
elif [[ "$OS" == "linux" ]]; then
|
||||
if [[ "$ARCH" == "x86_64" ]]; then
|
||||
@@ -122,12 +122,12 @@ elif [[ "$OS" == "linux" ]]; then
|
||||
if ldd --version 2>&1 | grep -qi glibc; then LIBC="gnu"; fi
|
||||
|
||||
if [[ "$LIBC" == "gnu" ]]; then
|
||||
ASSET_CANDIDATES+=("loki-x86_64-unknown-linux-gnu.tar.gz")
|
||||
ASSET_CANDIDATES+=("coyote-x86_64-unknown-linux-gnu.tar.gz")
|
||||
fi
|
||||
|
||||
ASSET_CANDIDATES+=("loki-x86_64-unknown-linux-musl.tar.gz")
|
||||
ASSET_CANDIDATES+=("coyote-x86_64-unknown-linux-musl.tar.gz")
|
||||
else
|
||||
ASSET_CANDIDATES+=("loki-aarch64-unknown-linux-musl.tar.gz")
|
||||
ASSET_CANDIDATES+=("coyote-aarch64-unknown-linux-musl.tar.gz")
|
||||
fi
|
||||
else
|
||||
echo "Error: unsupported OS for this installer: $OS" >&2; exit 1
|
||||
@@ -170,9 +170,9 @@ log "Download URL: $ASSET_URL"
|
||||
|
||||
ARCHIVE="$TMPDIR/asset"
|
||||
if [[ "$DL" == "curl" ]]; then
|
||||
curl -fL -H 'User-Agent: loki-installer' "$ASSET_URL" -o "$ARCHIVE"
|
||||
curl -fL -H 'User-Agent: coyote-installer' "$ASSET_URL" -o "$ARCHIVE"
|
||||
else
|
||||
wget -q --header='User-Agent: loki-installer' "$ASSET_URL" -O "$ARCHIVE"
|
||||
wget -q --header='User-Agent: coyote-installer' "$ASSET_URL" -O "$ARCHIVE"
|
||||
fi
|
||||
|
||||
WORK="$TMPDIR/work"; mkdir -p "$WORK"
|
||||
@@ -192,21 +192,21 @@ fi
|
||||
BIN_PATH=""
|
||||
while IFS= read -r -d '' f; do
|
||||
base=$(basename "$f")
|
||||
if [[ "$base" == "loki" ]]; then
|
||||
if [[ "$base" == "coyote" ]]; then
|
||||
BIN_PATH="$f"
|
||||
break
|
||||
fi
|
||||
done < <(find "$EXTRACTED_DIR" -type f -print0)
|
||||
|
||||
if [[ -z "$BIN_PATH" ]]; then
|
||||
echo "Error: could not find 'loki' binary in the archive" >&2
|
||||
echo "Error: could not find 'coyote' binary in the archive" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
chmod +x "$BIN_PATH"
|
||||
install -m 0755 "$BIN_PATH" "${BIN_DIR}/loki"
|
||||
install -m 0755 "$BIN_PATH" "${BIN_DIR}/coyote"
|
||||
|
||||
log "Installed: ${BIN_DIR}/loki"
|
||||
log "Installed: ${BIN_DIR}/coyote"
|
||||
|
||||
case ":$PATH:" in
|
||||
*":${BIN_DIR}:"*) ;;
|
||||
@@ -216,5 +216,5 @@ case ":$PATH:" in
|
||||
;;
|
||||
esac
|
||||
|
||||
log "Done. Try: loki --help"
|
||||
log "Done. Try: coyote --help"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
_loki_bash() {
|
||||
_coyote_bash() {
|
||||
if [[ -n "$READLINE_LINE" ]]; then
|
||||
READLINE_LINE=$(loki -e "$READLINE_LINE")
|
||||
READLINE_LINE=$(coyote -e "$READLINE_LINE")
|
||||
READLINE_POINT=${#READLINE_LINE}
|
||||
fi
|
||||
}
|
||||
bind -x '"\ee": _loki_bash'
|
||||
bind -x '"\ee": _coyote_bash'
|
||||
@@ -1,7 +1,7 @@
|
||||
fn _loki_elvish {
|
||||
fn _coyote_elvish {
|
||||
var line = (edit:current-command)
|
||||
var new-line = (loki -e $line)
|
||||
var new-line = (coyote -e $line)
|
||||
edit:replace-input $new-line
|
||||
}
|
||||
|
||||
edit:insert:binding[Alt-e] = $_loki_elvish
|
||||
edit:insert:binding[Alt-e] = $_coyote_elvish
|
||||
@@ -1,9 +1,9 @@
|
||||
function _loki_fish
|
||||
function _coyote_fish
|
||||
set -l _old (commandline)
|
||||
if test -n $_old
|
||||
echo -n "⌛"
|
||||
commandline -f repaint
|
||||
commandline (loki -e $_old)
|
||||
commandline (coyote -e $_old)
|
||||
end
|
||||
end
|
||||
bind \ee _loki_fish
|
||||
bind \ee _coyote_fish
|
||||
@@ -1,20 +1,20 @@
|
||||
def _loki_nushell [] {
|
||||
def _coyote_nushell [] {
|
||||
let _prev = (commandline)
|
||||
if ($_prev != "") {
|
||||
print '⌛'
|
||||
commandline edit -r (loki -e $_prev)
|
||||
commandline edit -r (coyote -e $_prev)
|
||||
}
|
||||
}
|
||||
|
||||
$env.config.keybindings = ($env.config.keybindings | append {
|
||||
name: loki_integration
|
||||
name: coyote_integration
|
||||
modifier: alt
|
||||
keycode: char_e
|
||||
mode: [emacs, vi_insert]
|
||||
event:[
|
||||
{
|
||||
send: executehostcommand,
|
||||
cmd: "_loki_nushell"
|
||||
cmd: "_coyote_nushell"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ Set-PSReadLineKeyHandler -Chord "alt+e" -ScriptBlock {
|
||||
[Microsoft.PowerShell.PSConsoleReadline]::GetBufferState([ref]$_old, [ref]$null)
|
||||
if ($_old) {
|
||||
[Microsoft.PowerShell.PSConsoleReadLine]::Insert('⌛')
|
||||
$_new = (loki -e $_old)
|
||||
$_new = (coyote -e $_old)
|
||||
[Microsoft.PowerShell.PSConsoleReadLine]::DeleteLine()
|
||||
[Microsoft.PowerShell.PSConsoleReadline]::Insert($_new)
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
_loki_zsh() {
|
||||
_coyote_zsh() {
|
||||
if [[ -n "$BUFFER" ]]; then
|
||||
local _old=$BUFFER
|
||||
BUFFER+="⌛"
|
||||
zle -I && zle redisplay
|
||||
BUFFER=$(loki -e "$_old")
|
||||
BUFFER=$(coyote -e "$_old")
|
||||
zle end-of-line
|
||||
fi
|
||||
}
|
||||
zle -N _loki_zsh
|
||||
bindkey '\ee' _loki_zsh
|
||||
zle -N _coyote_zsh
|
||||
bindkey '\ee' _coyote_zsh
|
||||
@@ -9,7 +9,7 @@ use std::env;
|
||||
use std::ffi::OsStr;
|
||||
use std::io;
|
||||
|
||||
const LOKI_CLI_NAME: &str = "loki";
|
||||
const COYOTE_CLI_NAME: &str = "coyote";
|
||||
|
||||
#[derive(Clone, Copy, Debug, clap::ValueEnum)]
|
||||
pub enum ShellCompletion {
|
||||
@@ -24,12 +24,14 @@ pub enum ShellCompletion {
|
||||
impl ShellCompletion {
|
||||
pub fn generate_completions(self, cmd: &mut clap::Command) {
|
||||
match self {
|
||||
Self::Bash => generate(Shell::Bash, cmd, LOKI_CLI_NAME, &mut io::stdout()),
|
||||
Self::Elvish => generate(Shell::Elvish, cmd, LOKI_CLI_NAME, &mut io::stdout()),
|
||||
Self::Fish => generate(Shell::Fish, cmd, LOKI_CLI_NAME, &mut io::stdout()),
|
||||
Self::PowerShell => generate(Shell::PowerShell, cmd, LOKI_CLI_NAME, &mut io::stdout()),
|
||||
Self::Zsh => generate(Shell::Zsh, cmd, LOKI_CLI_NAME, &mut io::stdout()),
|
||||
Self::Nushell => generate(Nushell, cmd, LOKI_CLI_NAME, &mut io::stdout()),
|
||||
Self::Bash => generate(Shell::Bash, cmd, COYOTE_CLI_NAME, &mut io::stdout()),
|
||||
Self::Elvish => generate(Shell::Elvish, cmd, COYOTE_CLI_NAME, &mut io::stdout()),
|
||||
Self::Fish => generate(Shell::Fish, cmd, COYOTE_CLI_NAME, &mut io::stdout()),
|
||||
Self::PowerShell => {
|
||||
generate(Shell::PowerShell, cmd, COYOTE_CLI_NAME, &mut io::stdout())
|
||||
}
|
||||
Self::Zsh => generate(Shell::Zsh, cmd, COYOTE_CLI_NAME, &mut io::stdout()),
|
||||
Self::Nushell => generate(Nushell, cmd, COYOTE_CLI_NAME, &mut io::stdout()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+10
-10
@@ -15,7 +15,7 @@ use std::io::{Read, stdin};
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(author, version, about, long_about = None)]
|
||||
#[command(
|
||||
name = "loki",
|
||||
name = "coyote",
|
||||
author = crate_authors!(),
|
||||
version = crate_version!(),
|
||||
about = crate_description!(),
|
||||
@@ -125,19 +125,19 @@ pub struct Cli {
|
||||
/// Disable colored log output
|
||||
#[arg(long, requires = "tail_logs")]
|
||||
pub disable_log_colors: bool,
|
||||
/// Add a secret to the Loki vault
|
||||
/// Add a secret to the Coyote vault
|
||||
#[arg(long, value_name = "SECRET_NAME", exclusive = true)]
|
||||
pub add_secret: Option<String>,
|
||||
/// Decrypt a secret from the Loki vault and print the plaintext
|
||||
/// Decrypt a secret from the Coyote vault and print the plaintext
|
||||
#[arg(long, value_name = "SECRET_NAME", exclusive = true, add = ArgValueCompleter::new(secrets_completer))]
|
||||
pub get_secret: Option<String>,
|
||||
/// Update an existing secret in the Loki vault
|
||||
/// Update an existing secret in the Coyote vault
|
||||
#[arg(long, value_name = "SECRET_NAME", exclusive = true, add = ArgValueCompleter::new(secrets_completer))]
|
||||
pub update_secret: Option<String>,
|
||||
/// Delete a secret from the Loki vault
|
||||
/// Delete a secret from the Coyote vault
|
||||
#[arg(long, value_name = "SECRET_NAME", exclusive = true, add = ArgValueCompleter::new(secrets_completer))]
|
||||
pub delete_secret: Option<String>,
|
||||
/// List all secrets stored in the Loki vault
|
||||
/// List all secrets stored in the Coyote vault
|
||||
#[arg(long, exclusive = true)]
|
||||
pub list_secrets: bool,
|
||||
/// Authenticate with an LLM provider using OAuth (e.g., --authenticate client_name)
|
||||
@@ -146,10 +146,10 @@ pub struct Cli {
|
||||
/// Generate static shell completion scripts
|
||||
#[arg(long, value_name = "SHELL", value_enum)]
|
||||
pub completions: Option<ShellCompletion>,
|
||||
/// Update Loki to the latest release, or to a specific version
|
||||
/// Update Coyote to the latest release, or to a specific version
|
||||
#[arg(long, value_name = "VERSION")]
|
||||
pub update: Option<Option<String>>,
|
||||
/// With --update, update even if Loki was installed via a package manager
|
||||
/// With --update, update even if Coyote was installed via a package manager
|
||||
#[arg(long, requires = "update")]
|
||||
pub force: bool,
|
||||
}
|
||||
@@ -202,7 +202,7 @@ mod tests {
|
||||
use clap::Parser;
|
||||
|
||||
fn parse(args: &[&str]) -> Cli {
|
||||
let mut full_args = vec!["loki"];
|
||||
let mut full_args = vec!["coyote"];
|
||||
full_args.extend_from_slice(args);
|
||||
Cli::try_parse_from(full_args).unwrap()
|
||||
}
|
||||
@@ -436,6 +436,6 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn parse_force_without_update_fails() {
|
||||
assert!(Cli::try_parse_from(["loki", "--force"]).is_err());
|
||||
assert!(Cli::try_parse_from(["coyote", "--force"]).is_err());
|
||||
}
|
||||
}
|
||||
|
||||
+23
-29
@@ -85,7 +85,7 @@ async fn prepare_chat_completions(
|
||||
let ready = oauth::prepare_oauth_access_token(client, &provider, self_.name()).await?;
|
||||
if !ready {
|
||||
bail!(
|
||||
"OAuth configured but no tokens found for '{}'. Run: 'loki --authenticate {}' or '.authenticate' in the REPL",
|
||||
"OAuth configured but no tokens found for '{}'. Run: 'coyote --authenticate {}' or '.authenticate' in the REPL",
|
||||
self_.name(),
|
||||
self_.name()
|
||||
);
|
||||
@@ -100,7 +100,7 @@ async fn prepare_chat_completions(
|
||||
request_data.header("x-api-key", api_key);
|
||||
} else {
|
||||
bail!(
|
||||
"No authentication configured for '{}'. Set `api_key` or use `auth: oauth` with `loki --authenticate {}`.",
|
||||
"No authentication configured for '{}'. Set `api_key` or use `auth: oauth` with `coyote --authenticate {}`.",
|
||||
self_.name(),
|
||||
self_.name()
|
||||
);
|
||||
@@ -114,41 +114,35 @@ async fn prepare_chat_completions(
|
||||
///
|
||||
/// This behavior was discovered 2026-03-17.
|
||||
///
|
||||
/// So this function injects the Claude Code system prompt into the request
|
||||
/// body to make it a valid request.
|
||||
/// The prefix must be in its **own** top-level system block. Concatenating it
|
||||
/// with role / session content into a single block causes Anthropic to reject
|
||||
/// the request with `rate_limit_error`. Any pre-existing system content is
|
||||
/// preserved as additional blocks after the prefix.
|
||||
fn inject_oauth_system_prompt(body: &mut Value) {
|
||||
let existing_text = match body.get("system") {
|
||||
let existing_blocks: Vec<Value> = match body.get("system") {
|
||||
Some(Value::String(s)) => {
|
||||
if s.starts_with(CLAUDE_CODE_PREFIX) {
|
||||
return;
|
||||
if s.is_empty() {
|
||||
Vec::new()
|
||||
} else {
|
||||
vec![json!({ "type": "text", "text": s })]
|
||||
}
|
||||
(!s.is_empty()).then(|| s.clone())
|
||||
}
|
||||
Some(Value::Array(blocks)) => {
|
||||
let already_injected = blocks.iter().any(|b| {
|
||||
b.get("text")
|
||||
.and_then(|t| t.as_str())
|
||||
.map(|t| t.starts_with(CLAUDE_CODE_PREFIX))
|
||||
.unwrap_or(false)
|
||||
});
|
||||
Some(Value::Array(blocks)) => blocks.clone(),
|
||||
_ => Vec::new(),
|
||||
};
|
||||
|
||||
let already_injected = existing_blocks
|
||||
.first()
|
||||
.and_then(|b| b.get("text").and_then(|t| t.as_str()))
|
||||
.map(|t| t == CLAUDE_CODE_PREFIX)
|
||||
.unwrap_or(false);
|
||||
if already_injected {
|
||||
return;
|
||||
}
|
||||
let joined: Vec<String> = blocks
|
||||
.iter()
|
||||
.filter_map(|b| b.get("text").and_then(|t| t.as_str()).map(String::from))
|
||||
.collect();
|
||||
(!joined.is_empty()).then(|| joined.join("\n\n"))
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let merged = match existing_text {
|
||||
Some(rest) => format!("{}\n\n{}", CLAUDE_CODE_PREFIX, rest),
|
||||
None => CLAUDE_CODE_PREFIX.to_string(),
|
||||
};
|
||||
|
||||
body["system"] = json!([{ "type": "text", "text": merged }]);
|
||||
let mut system = vec![json!({ "type": "text", "text": CLAUDE_CODE_PREFIX })];
|
||||
system.extend(existing_blocks);
|
||||
body["system"] = Value::Array(system);
|
||||
}
|
||||
|
||||
pub async fn claude_chat_completions(
|
||||
|
||||
@@ -111,7 +111,7 @@ async fn prepare_chat_completions(
|
||||
let ready = oauth::prepare_oauth_access_token(client, &provider, self_.name()).await?;
|
||||
if !ready {
|
||||
bail!(
|
||||
"OAuth configured but no tokens found for '{}'. Run: 'loki --authenticate {}' or '.authenticate' in the REPL",
|
||||
"OAuth configured but no tokens found for '{}'. Run: 'coyote --authenticate {}' or '.authenticate' in the REPL",
|
||||
self_.name(),
|
||||
self_.name()
|
||||
);
|
||||
@@ -122,7 +122,7 @@ async fn prepare_chat_completions(
|
||||
request_data.header("x-goog-api-key", api_key);
|
||||
} else {
|
||||
bail!(
|
||||
"No authentication configured for '{}'. Set `api_key` or use `auth: oauth` with `loki --authenticate {}`.",
|
||||
"No authentication configured for '{}'. Set `api_key` or use `auth: oauth` with `coyote --authenticate {}`.",
|
||||
self_.name(),
|
||||
self_.name()
|
||||
);
|
||||
@@ -181,7 +181,7 @@ async fn prepare_embeddings(
|
||||
let ready = oauth::prepare_oauth_access_token(client, &provider, self_.name()).await?;
|
||||
if !ready {
|
||||
bail!(
|
||||
"OAuth configured but no tokens found for '{}'. Run: 'loki --authenticate {}' or '.authenticate' in the REPL",
|
||||
"OAuth configured but no tokens found for '{}'. Run: 'coyote --authenticate {}' or '.authenticate' in the REPL",
|
||||
self_.name(),
|
||||
self_.name()
|
||||
);
|
||||
|
||||
+1
-1
@@ -526,7 +526,7 @@ impl RoleLike for Agent {
|
||||
}
|
||||
|
||||
fn enabled_tools(&self) -> Option<String> {
|
||||
self.config.global_tools.clone().join(",").into()
|
||||
None
|
||||
}
|
||||
|
||||
fn enabled_mcp_servers(&self) -> Option<String> {
|
||||
|
||||
+2
-2
@@ -879,7 +879,7 @@ mod tests {
|
||||
#[test]
|
||||
fn from_files_loads_single_text_file() {
|
||||
let dir = env::temp_dir().join(format!(
|
||||
"loki-input-test-{}",
|
||||
"coyote-input-test-{}",
|
||||
SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.unwrap()
|
||||
@@ -906,7 +906,7 @@ mod tests {
|
||||
#[test]
|
||||
fn from_files_loads_multiple_files() {
|
||||
let dir = env::temp_dir().join(format!(
|
||||
"loki-input-test-multi-{}",
|
||||
"coyote-input-test-multi-{}",
|
||||
SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.unwrap()
|
||||
|
||||
@@ -6,6 +6,7 @@ use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use crate::config::{InstallFilter, paths};
|
||||
#[cfg(not(windows))]
|
||||
use crate::function::Language;
|
||||
use crate::mcp::{McpServer, McpServersConfig};
|
||||
use crate::utils;
|
||||
@@ -135,7 +136,7 @@ impl Drop for TempRepoDir {
|
||||
}
|
||||
|
||||
fn clone_to_temp(url: &str, reference: Option<&str>) -> Result<TempRepoDir> {
|
||||
let dest = utils::temp_file("loki-remote-install-", "");
|
||||
let dest = utils::temp_file("coyote-remote-install-", "");
|
||||
let dest_arg: OsString = dest.as_os_str().into();
|
||||
|
||||
let is_sha = reference
|
||||
@@ -874,7 +875,7 @@ fn print_secret_summary(added: &[String], deferred: &[String]) {
|
||||
if !deferred.is_empty() {
|
||||
println!(
|
||||
"\nThe following secrets are still required by your MCP servers. \
|
||||
Add them with `loki --add-secret <NAME>` or `.vault add <NAME>` in the REPL:"
|
||||
Add them with `coyote --add-secret <NAME>` or `.vault add <NAME>` in the REPL:"
|
||||
);
|
||||
for name in deferred {
|
||||
println!(" {{{{ {name} }}}}");
|
||||
@@ -1264,12 +1265,12 @@ mod tests {
|
||||
let target = dir.join("target.json");
|
||||
write_mcp(
|
||||
&remote,
|
||||
r#"{"mcpServers": {"x": {"type":"stdio","command":"echo","env":{"K":"{{LOKI_TEST_MERGE_SECRET}}"}}}}"#,
|
||||
r#"{"mcpServers": {"x": {"type":"stdio","command":"echo","env":{"K":"{{COYOTE_TEST_MERGE_SECRET}}"}}}}"#,
|
||||
);
|
||||
|
||||
let report = merge_mcp_json(None, &remote, &target, false).unwrap();
|
||||
|
||||
assert_eq!(report.missing_secrets, vec!["LOKI_TEST_MERGE_SECRET"]);
|
||||
assert_eq!(report.missing_secrets, vec!["COYOTE_TEST_MERGE_SECRET"]);
|
||||
let _ = fs::remove_dir_all(&dir);
|
||||
}
|
||||
|
||||
@@ -1299,8 +1300,8 @@ mod tests {
|
||||
#[test]
|
||||
fn handle_missing_secrets_defers_all_in_non_tty() {
|
||||
let missing = vec![
|
||||
"LOKI_TEST_STEP4_A".to_string(),
|
||||
"LOKI_TEST_STEP4_B".to_string(),
|
||||
"COYOTE_TEST_STEP4_A".to_string(),
|
||||
"COYOTE_TEST_STEP4_B".to_string(),
|
||||
];
|
||||
|
||||
assert!(handle_missing_secrets(&missing).is_ok());
|
||||
|
||||
+3
-3
@@ -104,13 +104,13 @@ const DEFAULT_VISIBLE_TOOLS: [&str; 18] = [
|
||||
"get_current_weather.sh",
|
||||
"search_wikipedia.sh",
|
||||
"search_arxiv.sh",
|
||||
"web_search_loki.sh",
|
||||
"web_search_coyote.sh",
|
||||
];
|
||||
|
||||
const CLIENTS_FIELD: &str = "clients";
|
||||
|
||||
const SYNC_MODELS_URL: &str =
|
||||
"https://raw.githubusercontent.com/Dark-Alex-17/loki/refs/heads/main/models.yaml";
|
||||
"https://raw.githubusercontent.com/Dark-Alex-17/coyote/refs/heads/main/models.yaml";
|
||||
|
||||
const SUMMARIZATION_PROMPT: &str =
|
||||
"Summarize the discussion briefly in 200 words or less to use as a prompt for future context.";
|
||||
@@ -625,7 +625,7 @@ pub async fn create_config_file(config_path: &Path) -> Result<()> {
|
||||
|
||||
let config_data = serde_yaml::to_string(&config).with_context(|| "Failed to create config")?;
|
||||
let config_data = format!(
|
||||
"# see https://github.com/Dark-Alex-17/loki/blob/main/config.example.yaml\n\n{config_data}"
|
||||
"# see https://github.com/Dark-Alex-17/coyote/blob/main/config.example.yaml\n\n{config_data}"
|
||||
);
|
||||
|
||||
ensure_parent_exists(config_path)?;
|
||||
|
||||
@@ -1498,7 +1498,7 @@ impl RequestContext {
|
||||
if !target_path.exists() {
|
||||
fs::write(
|
||||
&target_path,
|
||||
"# see https://github.com/Dark-Alex-17/loki/blob/main/config.agent.example.yaml\n",
|
||||
"# see https://github.com/Dark-Alex-17/coyote/blob/main/config.agent.example.yaml\n",
|
||||
)
|
||||
.with_context(|| format!("Failed to write to '{}'", target_path.display()))?;
|
||||
}
|
||||
@@ -2706,7 +2706,7 @@ mod tests {
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
let path = env::temp_dir().join(format!("loki-request-context-tests-{unique}"));
|
||||
let path = env::temp_dir().join(format!("coyote-request-context-tests-{unique}"));
|
||||
create_dir_all(&path).unwrap();
|
||||
unsafe {
|
||||
env::set_var(&key, &path);
|
||||
@@ -2969,6 +2969,7 @@ mod tests {
|
||||
#[test]
|
||||
#[serial]
|
||||
fn rebuild_tool_scope_mcp_disabled_skips_servers() {
|
||||
let _guard = TestConfigDirGuard::new();
|
||||
let app_state = app_state_with_mcp_config(false, &["github", "slack"]);
|
||||
let mut ctx = RequestContext::new(app_state, WorkingMode::Cmd);
|
||||
let app = ctx.app.config.clone();
|
||||
@@ -2982,6 +2983,7 @@ mod tests {
|
||||
#[test]
|
||||
#[serial]
|
||||
fn rebuild_tool_scope_no_enabled_servers_yields_empty_runtime() {
|
||||
let _guard = TestConfigDirGuard::new();
|
||||
let app_state = app_state_with_mcp_config(true, &["github"]);
|
||||
let mut ctx = RequestContext::new(app_state, WorkingMode::Cmd);
|
||||
let app = ctx.app.config.clone();
|
||||
@@ -2995,6 +2997,7 @@ mod tests {
|
||||
#[test]
|
||||
#[serial]
|
||||
fn rebuild_tool_scope_no_mcp_config_yields_empty_runtime() {
|
||||
let _guard = TestConfigDirGuard::new();
|
||||
let app_state = app_state_with_mcp_config(true, &[]);
|
||||
let mut ctx = RequestContext::new(app_state, WorkingMode::Cmd);
|
||||
let app = ctx.app.config.clone();
|
||||
@@ -3008,6 +3011,7 @@ mod tests {
|
||||
#[test]
|
||||
#[serial]
|
||||
fn rebuild_tool_scope_preserves_tool_tracker() {
|
||||
let _guard = TestConfigDirGuard::new();
|
||||
let app_state = app_state_with_mcp_config(false, &[]);
|
||||
let mut ctx = RequestContext::new(app_state, WorkingMode::Cmd);
|
||||
let dummy = ToolCall {
|
||||
@@ -3035,6 +3039,7 @@ mod tests {
|
||||
#[test]
|
||||
#[serial]
|
||||
fn rebuild_tool_scope_repl_mode_appends_user_interaction_functions() {
|
||||
let _guard = TestConfigDirGuard::new();
|
||||
let app_state = app_state_with_mcp_config(false, &[]);
|
||||
let mut ctx = RequestContext::new(app_state, WorkingMode::Repl);
|
||||
let app = ctx.app.config.clone();
|
||||
@@ -3058,6 +3063,7 @@ mod tests {
|
||||
#[test]
|
||||
#[serial]
|
||||
fn rebuild_tool_scope_cmd_mode_no_user_interaction_functions() {
|
||||
let _guard = TestConfigDirGuard::new();
|
||||
let app_state = app_state_with_mcp_config(false, &[]);
|
||||
let mut ctx = RequestContext::new(app_state, WorkingMode::Cmd);
|
||||
let app = ctx.app.config.clone();
|
||||
|
||||
+17
-17
@@ -69,7 +69,7 @@ fn normalize_version(requested: Option<String>) -> Option<String> {
|
||||
}
|
||||
|
||||
fn is_dir_writable(dir: &Path) -> bool {
|
||||
let probe = dir.join(format!(".loki-update-write-test-{}", process::id()));
|
||||
let probe = dir.join(format!(".coyote-update-write-test-{}", process::id()));
|
||||
match OpenOptions::new().write(true).create_new(true).open(&probe) {
|
||||
Ok(_) => {
|
||||
let _ = fs::remove_file(&probe);
|
||||
@@ -83,24 +83,24 @@ pub fn run_self_update(requested: Option<String>, force: bool) -> Result<()> {
|
||||
let target_tag = normalize_version(requested);
|
||||
|
||||
let exe_path = env::current_exe()
|
||||
.context("Could not determine the path of the running loki executable")?;
|
||||
.context("Could not determine the path of the running coyote executable")?;
|
||||
let resolved = canonicalize(&exe_path).unwrap_or_else(|_| exe_path.clone());
|
||||
let source = classify_install_path(&resolved);
|
||||
|
||||
if source.is_package_managed() {
|
||||
let body = match source {
|
||||
InstallSource::Homebrew => format!(
|
||||
"Loki appears to be installed via Homebrew ({}).\n\
|
||||
"Coyote appears to be installed via Homebrew ({}).\n\
|
||||
Updating in place replaces the binary inside Homebrew's Cellar; `brew` will\n\
|
||||
then report a version that no longer matches the file on disk, and a later\n\
|
||||
`brew upgrade`/`brew reinstall` may overwrite it or fail.\n\
|
||||
The clean way to update is: brew upgrade loki",
|
||||
The clean way to update is: brew upgrade coyote",
|
||||
exe_path.display()
|
||||
),
|
||||
InstallSource::Cargo => format!(
|
||||
"Loki appears to be installed via `cargo install` ({}).\n\
|
||||
"Coyote appears to be installed via `cargo install` ({}).\n\
|
||||
Updating in place leaves Cargo's records out of sync with the binary on disk.\n\
|
||||
The clean way to update is: cargo install --locked loki-ai",
|
||||
The clean way to update is: cargo install --locked coyote-ai",
|
||||
exe_path.display()
|
||||
),
|
||||
InstallSource::Manual => unreachable!("Manual installs are not package-managed"),
|
||||
@@ -130,7 +130,7 @@ pub fn run_self_update(requested: Option<String>, force: bool) -> Result<()> {
|
||||
{
|
||||
bail!(
|
||||
"No write permission for '{}'. Re-run with elevated permissions (e.g. sudo), \
|
||||
or update Loki through your package manager.",
|
||||
or update Coyote through your package manager.",
|
||||
parent.display()
|
||||
);
|
||||
}
|
||||
@@ -139,8 +139,8 @@ pub fn run_self_update(requested: Option<String>, force: bool) -> Result<()> {
|
||||
let mut builder = Update::configure();
|
||||
builder
|
||||
.repo_owner("Dark-Alex-17")
|
||||
.repo_name("loki")
|
||||
.bin_name("loki")
|
||||
.repo_name("coyote")
|
||||
.bin_name("coyote")
|
||||
.current_version(env!("CARGO_PKG_VERSION"))
|
||||
.no_confirm(true)
|
||||
.show_download_progress(interactive);
|
||||
@@ -155,10 +155,10 @@ pub fn run_self_update(requested: Option<String>, force: bool) -> Result<()> {
|
||||
|
||||
match status {
|
||||
Status::UpToDate(version) => {
|
||||
println!("Loki is already up to date (v{version}).");
|
||||
println!("Coyote is already up to date (v{version}).");
|
||||
}
|
||||
Status::Updated(version) => {
|
||||
println!("Loki updated to v{version}. Restart loki to use the new version.");
|
||||
println!("Coyote updated to v{version}. Restart coyote to use the new version.");
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
@@ -172,7 +172,7 @@ mod tests {
|
||||
#[test]
|
||||
fn classify_cargo_install() {
|
||||
assert_eq!(
|
||||
classify_install_path(&PathBuf::from("/home/u/.cargo/bin/loki")),
|
||||
classify_install_path(&PathBuf::from("/home/u/.cargo/bin/coyote")),
|
||||
InstallSource::Cargo
|
||||
);
|
||||
}
|
||||
@@ -180,7 +180,7 @@ mod tests {
|
||||
#[test]
|
||||
fn classify_homebrew_opt_prefix() {
|
||||
assert_eq!(
|
||||
classify_install_path(&PathBuf::from("/opt/homebrew/bin/loki")),
|
||||
classify_install_path(&PathBuf::from("/opt/homebrew/bin/coyote")),
|
||||
InstallSource::Homebrew
|
||||
);
|
||||
}
|
||||
@@ -188,7 +188,7 @@ mod tests {
|
||||
#[test]
|
||||
fn classify_homebrew_cellar() {
|
||||
assert_eq!(
|
||||
classify_install_path(&PathBuf::from("/usr/local/Cellar/loki/0.3.0/bin/loki")),
|
||||
classify_install_path(&PathBuf::from("/usr/local/Cellar/coyote/0.3.0/bin/coyote")),
|
||||
InstallSource::Homebrew
|
||||
);
|
||||
}
|
||||
@@ -196,7 +196,7 @@ mod tests {
|
||||
#[test]
|
||||
fn classify_homebrew_linuxbrew() {
|
||||
assert_eq!(
|
||||
classify_install_path(&PathBuf::from("/home/linuxbrew/.linuxbrew/bin/loki")),
|
||||
classify_install_path(&PathBuf::from("/home/linuxbrew/.linuxbrew/bin/coyote")),
|
||||
InstallSource::Homebrew
|
||||
);
|
||||
}
|
||||
@@ -204,7 +204,7 @@ mod tests {
|
||||
#[test]
|
||||
fn classify_manual_usr_local_bin() {
|
||||
assert_eq!(
|
||||
classify_install_path(&PathBuf::from("/usr/local/bin/loki")),
|
||||
classify_install_path(&PathBuf::from("/usr/local/bin/coyote")),
|
||||
InstallSource::Manual
|
||||
);
|
||||
}
|
||||
@@ -212,7 +212,7 @@ mod tests {
|
||||
#[test]
|
||||
fn classify_manual_local_bin() {
|
||||
assert_eq!(
|
||||
classify_install_path(&PathBuf::from("/home/u/.local/bin/loki")),
|
||||
classify_install_path(&PathBuf::from("/home/u/.local/bin/coyote")),
|
||||
InstallSource::Manual
|
||||
);
|
||||
}
|
||||
|
||||
+27
-5
@@ -4,6 +4,7 @@ pub(crate) mod user_interaction;
|
||||
|
||||
use crate::{
|
||||
config::{Agent, RequestContext},
|
||||
graph,
|
||||
utils::*,
|
||||
};
|
||||
|
||||
@@ -1199,6 +1200,9 @@ pub fn run_llm_function(
|
||||
if dir.exists() {
|
||||
bin_dirs.push(dir);
|
||||
}
|
||||
if graph::agent_has_graph(&agent_name) {
|
||||
envs.insert("AUTO_CONFIRM".into(), "true".into());
|
||||
}
|
||||
} else {
|
||||
bin_dirs.push(paths::functions_bin_dir());
|
||||
}
|
||||
@@ -1242,7 +1246,7 @@ pub fn run_llm_function(
|
||||
.map_err(|err| anyhow!("Unable to run {command_name}, {err}"))?;
|
||||
|
||||
let stdout = child.stdout.take().expect("Failed to capture stdout");
|
||||
let mut stderr = child.stderr.take().expect("Failed to capture stderr");
|
||||
let stderr = child.stderr.take().expect("Failed to capture stderr");
|
||||
|
||||
let stdout_thread = std::thread::spawn(move || {
|
||||
let mut buffer = [0; 1024];
|
||||
@@ -1269,8 +1273,29 @@ pub fn run_llm_function(
|
||||
});
|
||||
|
||||
let stderr_thread = std::thread::spawn(move || {
|
||||
let mut buffer = [0; 1024];
|
||||
let mut reader = stderr;
|
||||
let mut err = io::stderr();
|
||||
let mut buf = Vec::new();
|
||||
let _ = stderr.read_to_end(&mut buf);
|
||||
while let Ok(n) = reader.read(&mut buffer) {
|
||||
if n == 0 {
|
||||
break;
|
||||
}
|
||||
let chunk = &buffer[0..n];
|
||||
buf.extend_from_slice(chunk);
|
||||
let mut last_pos = 0;
|
||||
for (i, &byte) in chunk.iter().enumerate() {
|
||||
if byte == b'\n' {
|
||||
let _ = err.write_all(&chunk[last_pos..i]);
|
||||
let _ = err.write_all(b"\r\n");
|
||||
last_pos = i + 1;
|
||||
}
|
||||
}
|
||||
if last_pos < n {
|
||||
let _ = err.write_all(&chunk[last_pos..n]);
|
||||
}
|
||||
let _ = err.flush();
|
||||
}
|
||||
buf
|
||||
});
|
||||
|
||||
@@ -1283,9 +1308,6 @@ pub fn run_llm_function(
|
||||
let exit_code = status.code().unwrap_or_default();
|
||||
if exit_code != 0 {
|
||||
let stderr = String::from_utf8_lossy(&stderr_bytes).trim().to_string();
|
||||
if !stderr.is_empty() {
|
||||
eprintln!("{stderr}");
|
||||
}
|
||||
let tool_error_message = format!("Tool call '{command_name}' exited with code {exit_code}");
|
||||
eprintln!("{}", warning_text(&format!("⚠️ {tool_error_message} ⚠️")));
|
||||
let mut error_json = json!({"tool_call_error": tool_error_message});
|
||||
|
||||
+34
-18
@@ -28,36 +28,46 @@ impl LlmNodeExecutor {
|
||||
parent_ctx: &mut RequestContext,
|
||||
) -> Result<LlmExecutionOutcome> {
|
||||
let result = run(node, state_manager, parent_ctx).await;
|
||||
let (output, failed) = match result {
|
||||
let (output, failure_reason) = match result {
|
||||
Ok(raw) => match &node.output_schema {
|
||||
Some(schema) => match structured::extract(&raw, schema, parent_ctx).await {
|
||||
Ok(value) => (value, false),
|
||||
Ok(value) => (value, None),
|
||||
Err(e) => {
|
||||
warn!("llm node structured extraction failed: {e}");
|
||||
(
|
||||
Value::String(format!("LLM node structured-extraction failed: {e}")),
|
||||
true,
|
||||
Some(format!("structured-extraction failed: {e}")),
|
||||
)
|
||||
}
|
||||
},
|
||||
None => (Value::String(raw), false),
|
||||
None => (Value::String(raw), None),
|
||||
},
|
||||
Err(e) => {
|
||||
warn!("llm node failed: {e}");
|
||||
(Value::String(format!("LLM node failed: {e}")), true)
|
||||
(
|
||||
Value::String(format!("LLM node failed: {e}")),
|
||||
Some(format!("LLM call failed: {e:#}")),
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
apply_state_updates_with_output(node, state_manager, &output);
|
||||
Ok(outcome_from(failed, node.fallback.as_deref()))
|
||||
outcome_from(failure_reason.as_deref(), node.fallback.as_deref())
|
||||
}
|
||||
}
|
||||
|
||||
fn outcome_from(failed: bool, fallback: Option<&str>) -> LlmExecutionOutcome {
|
||||
if failed && let Some(fb) = fallback {
|
||||
LlmExecutionOutcome::FellBack(fb.to_string())
|
||||
} else {
|
||||
LlmExecutionOutcome::Continue
|
||||
fn outcome_from(
|
||||
failure_reason: Option<&str>,
|
||||
fallback: Option<&str>,
|
||||
) -> Result<LlmExecutionOutcome> {
|
||||
match (failure_reason, fallback) {
|
||||
(None, _) => Ok(LlmExecutionOutcome::Continue),
|
||||
(Some(_), Some(fb)) => Ok(LlmExecutionOutcome::FellBack(fb.to_string())),
|
||||
(Some(reason), None) => bail!(
|
||||
"LLM node failed and no fallback declared: {reason}. \
|
||||
Add a `fallback:` route on the node to route on failure, \
|
||||
or fix the underlying error."
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -445,23 +455,29 @@ mod tests {
|
||||
#[test]
|
||||
fn outcome_from_success_is_continue() {
|
||||
assert_eq!(
|
||||
outcome_from(false, Some("fb")),
|
||||
outcome_from(None, Some("fb")).unwrap(),
|
||||
LlmExecutionOutcome::Continue
|
||||
);
|
||||
assert_eq!(
|
||||
outcome_from(None, None).unwrap(),
|
||||
LlmExecutionOutcome::Continue
|
||||
);
|
||||
assert_eq!(outcome_from(false, None), LlmExecutionOutcome::Continue);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn outcome_from_failure_with_fallback_is_fell_back() {
|
||||
assert_eq!(
|
||||
outcome_from(true, Some("fb")),
|
||||
outcome_from(Some("HTTP 404"), Some("fb")).unwrap(),
|
||||
LlmExecutionOutcome::FellBack("fb".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn outcome_from_failure_without_fallback_is_continue() {
|
||||
assert_eq!(outcome_from(true, None), LlmExecutionOutcome::Continue);
|
||||
fn outcome_from_failure_without_fallback_propagates_error() {
|
||||
let err = outcome_from(Some("HTTP 404"), None).unwrap_err();
|
||||
let msg = format!("{err:#}");
|
||||
assert!(msg.contains("no fallback declared"), "got: {msg}");
|
||||
assert!(msg.contains("HTTP 404"), "got: {msg}");
|
||||
}
|
||||
|
||||
fn node_with_schema(updates: Option<HashMap<String, String>>, schema: Value) -> LlmNode {
|
||||
@@ -552,13 +568,13 @@ mod tests {
|
||||
let entries = vec![
|
||||
"read_query".to_string(),
|
||||
"mcp:pubmed-search".to_string(),
|
||||
"web_search_loki".to_string(),
|
||||
"web_search_coyote".to_string(),
|
||||
"mcp:github".to_string(),
|
||||
];
|
||||
|
||||
let (regular, mcp) = categorize_tools(Some(&entries));
|
||||
|
||||
assert_eq!(regular, vec!["read_query", "web_search_loki"]);
|
||||
assert_eq!(regular, vec!["read_query", "web_search_coyote"]);
|
||||
assert_eq!(mcp, vec!["pubmed-search", "github"]);
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -423,7 +423,7 @@ mod tests {
|
||||
#[test]
|
||||
fn load_from_file_reads_disk() {
|
||||
let dir = env::temp_dir();
|
||||
let path = dir.join(format!("loki_graph_parser_test_{}.yaml", process::id()));
|
||||
let path = dir.join(format!("coyote_graph_parser_test_{}.yaml", process::id()));
|
||||
let yaml = formatdoc! {r#"
|
||||
name: disk_graph
|
||||
version: "1.0"
|
||||
|
||||
@@ -55,6 +55,7 @@ impl ScriptExecutor {
|
||||
cmd.stdout(Stdio::piped());
|
||||
cmd.stderr(Stdio::piped());
|
||||
cmd.envs(&self.extra_envs);
|
||||
cmd.env("AUTO_CONFIRM", "true");
|
||||
match &state_repr {
|
||||
StateRepresentation::Inline(json) => {
|
||||
cmd.env("GRAPH_STATE", json);
|
||||
|
||||
+2
-2
@@ -812,7 +812,7 @@ model: anthropic:claude-sonnet-4-6
|
||||
temperature: 0.2
|
||||
top_p: 0.9
|
||||
global_tools:
|
||||
- web_search_loki.sh
|
||||
- web_search_coyote.sh
|
||||
mcp_servers:
|
||||
- pubmed-search
|
||||
conversation_starters:
|
||||
@@ -827,7 +827,7 @@ nodes:
|
||||
assert_eq!(graph.model.as_deref(), Some("anthropic:claude-sonnet-4-6"));
|
||||
assert_eq!(graph.temperature, Some(0.2));
|
||||
assert_eq!(graph.top_p, Some(0.9));
|
||||
assert_eq!(graph.global_tools, vec!["web_search_loki.sh"]);
|
||||
assert_eq!(graph.global_tools, vec!["web_search_coyote.sh"]);
|
||||
assert_eq!(graph.mcp_servers, vec!["pubmed-search"]);
|
||||
assert_eq!(graph.conversation_starters, vec!["Look up 2160-0"]);
|
||||
}
|
||||
|
||||
@@ -369,7 +369,8 @@ mod tests {
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.expect("time went backwards")
|
||||
.as_nanos();
|
||||
let path = std::env::temp_dir().join(format!("loki_python_parser_{file_name}_{unique}.py"));
|
||||
let path =
|
||||
std::env::temp_dir().join(format!("coyote_python_parser_{file_name}_{unique}.py"));
|
||||
fs::write(&path, source).expect("failed to write temp python source");
|
||||
let file = File::open(&path).expect("failed to open temp python source");
|
||||
let result = generate_python_declarations(file, file_name, Some(parent));
|
||||
|
||||
@@ -425,7 +425,7 @@ mod tests {
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.expect("time")
|
||||
.as_nanos();
|
||||
let path = std::env::temp_dir().join(format!("loki_ts_parser_{file_name}_{unique}.ts"));
|
||||
let path = std::env::temp_dir().join(format!("coyote_ts_parser_{file_name}_{unique}.ts"));
|
||||
fs::write(&path, source).expect("write");
|
||||
let file = File::open(&path).expect("open");
|
||||
let result = generate_typescript_declarations(file, file_name, Some(parent));
|
||||
|
||||
+2
-2
@@ -215,7 +215,7 @@ static REPL_COMMANDS: LazyLock<[ReplCommand; 42]> = LazyLock::new(|| {
|
||||
),
|
||||
ReplCommand::new(
|
||||
".vault",
|
||||
"View or modify the Loki vault",
|
||||
"View or modify the Coyote vault",
|
||||
AssertState::pass(),
|
||||
),
|
||||
ReplCommand::new(
|
||||
@@ -225,7 +225,7 @@ static REPL_COMMANDS: LazyLock<[ReplCommand; 42]> = LazyLock::new(|| {
|
||||
),
|
||||
ReplCommand::new(
|
||||
".update",
|
||||
"Update Loki to the latest release (or a specified version)",
|
||||
"Update Coyote to the latest release (or a specified version)",
|
||||
AssertState::pass(),
|
||||
),
|
||||
ReplCommand::new(".exit", "Exit REPL", AssertState::pass()),
|
||||
|
||||
+1
-1
@@ -366,7 +366,7 @@ mod tests {
|
||||
assert!(is_valid_extension(Some(&md_ext), Path::new("Agents.md")));
|
||||
assert!(is_valid_extension(
|
||||
Some(&md_ext),
|
||||
Path::new("/home/atusa/code/loki.wiki/Agents.md")
|
||||
Path::new("/home/atusa/code/coyote.wiki/Agents.md")
|
||||
));
|
||||
assert!(!is_valid_extension(Some(&md_ext), Path::new("notes.txt")));
|
||||
assert!(!is_valid_extension(Some(&md_ext), Path::new("README")));
|
||||
|
||||
+2
-2
@@ -28,7 +28,7 @@ pub fn ensure_password_file_initialized(local_provider: &mut LocalProvider) -> R
|
||||
}
|
||||
} else {
|
||||
Err(anyhow!(
|
||||
"A password file is required to utilize the Loki vault. Please configure a password file in your config file and try again."
|
||||
"A password file is required to utilize the Coyote vault. Please configure a password file in your config file and try again."
|
||||
))
|
||||
}
|
||||
}
|
||||
@@ -95,7 +95,7 @@ pub fn create_vault_password_file(vault: &mut Vault) -> Result<()> {
|
||||
|
||||
if !ans {
|
||||
return Err(anyhow!(
|
||||
"A password file is required to utilize the Loki vault. Please configure a password file in your config file and try again."
|
||||
"A password file is required to utilize the Coyote vault. Please configure a password file in your config file and try again."
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user