GRAFT Code desktop app for Windows
Browse files- .gitattributes +1 -0
- GRAFT-Code.exe +3 -0
- README.md +88 -0
- graft-bridge/SKILL.md +36 -0
- graft-bridge/graft_bridge.py +74 -0
.gitattributes
CHANGED
|
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
GRAFT-Code.exe filter=lfs diff=lfs merge=lfs -text
|
GRAFT-Code.exe
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:509d96a2ccdae3d96908905e7f646140ad20a81fac609fe8ea562fcb249b8484
|
| 3 |
+
size 41694754
|
README.md
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
license: apache-2.0
|
| 3 |
+
language:
|
| 4 |
+
- en
|
| 5 |
+
tags:
|
| 6 |
+
- windows
|
| 7 |
+
- desktop-app
|
| 8 |
+
- agent
|
| 9 |
+
- tool-calling
|
| 10 |
+
- llama.cpp
|
| 11 |
+
- gguf
|
| 12 |
+
- local-ai
|
| 13 |
+
---
|
| 14 |
+
|
| 15 |
+
# GRAFT Code
|
| 16 |
+
|
| 17 |
+
**A local AI agent for Windows that actually does things.** GRAFT Code is a desktop app that runs a GRAFT model on
|
| 18 |
+
your own PC and lets it work on your files: it makes folders, writes and reads files, moves, renames, copies and
|
| 19 |
+
deletes things, runs commands and Python, does exact math, and searches and reads the web. Every change asks you
|
| 20 |
+
first. Nothing leaves your computer except web-search keywords.
|
| 21 |
+
|
| 22 |
+
Made by **SmallAICreator / UltraLabs**.
|
| 23 |
+
|
| 24 |
+
| File | What it is |
|
| 25 |
+
|---|---|
|
| 26 |
+
| `GRAFT-Code.exe` | GRAFT Code for **Windows 10/11** (x64, runs on CPU). llama.cpp is bundled. |
|
| 27 |
+
| `graft-bridge/` | Optional: a Claude Code skill that lets GRAFT ask Claude for help (`talk_to_claude`). |
|
| 28 |
+
|
| 29 |
+
## Pick a model
|
| 30 |
+
|
| 31 |
+
| Model | Size | Best for |
|
| 32 |
+
|---|---|---|
|
| 33 |
+
| [GRAFT-10B-A2B](https://huggingface.co/SmallAICreator/GRAFT-10B-A2B-GGUF) | 6.3 GB, 16 GB RAM | The smartest option: a 10B mixture-of-experts with only 2B active, perfect recall up to 32K tokens |
|
| 34 |
+
| [GRAFT-1B-Agentic](https://huggingface.co/SmallAICreator/GRAFT-1B-Agentic) | 1 GB, 8 GB RAM | Fast and light, tuned for tool use |
|
| 35 |
+
|
| 36 |
+
Put the `.gguf` in your **Downloads** folder and GRAFT Code finds it.
|
| 37 |
+
|
| 38 |
+
## Get started
|
| 39 |
+
|
| 40 |
+
1. Download `GRAFT-Code.exe` and a model (above).
|
| 41 |
+
2. Double-click the EXE. It's unsigned, so Windows SmartScreen may warn you: click *More info → Run anyway*.
|
| 42 |
+
3. Wait for the model to load. The first launch reads GRAFT's instructions once (about 2 minutes on a laptop CPU with
|
| 43 |
+
the 10B). Later launches restore them from disk in seconds.
|
| 44 |
+
4. Type what you want done, for example *"make a Python script that renames all my photos by date"*.
|
| 45 |
+
|
| 46 |
+
It needs Microsoft Edge WebView2, which Windows 10 and 11 already have. GRAFT works in `~/GRAFT-Workspace`; change
|
| 47 |
+
the folder from the sidebar.
|
| 48 |
+
|
| 49 |
+
## Features
|
| 50 |
+
|
| 51 |
+
- **Clean chat window** with light and dark themes. Replies stream in live, with formatted text and code blocks you
|
| 52 |
+
can copy.
|
| 53 |
+
- **Tool cards and approvals.** Each action (write, run, search…) shows up as a card you can expand. Changes need an
|
| 54 |
+
approval: **Yes**, **Yes, and don't ask again** or **No, tell GRAFT what to do instead**.
|
| 55 |
+
- **Model picker.** Searches every `.gguf` in your Downloads, Desktop, Documents and LM Studio folders, has
|
| 56 |
+
*Browse…* for anything else, and reopens the last model you used.
|
| 57 |
+
- **Personas.** GRAFT, Coder, Playful, Concise and Teacher, or make your own with an emoji. Each persona has its own
|
| 58 |
+
system prompt and sampling settings.
|
| 59 |
+
- **Settings.**
|
| 60 |
+
- *Sampling:* temperature, top-p, top-k, min-p, repeat penalty and reply length.
|
| 61 |
+
- *Model:* context size (up to what the model supports), CPU threads and the draft model.
|
| 62 |
+
- *Agent:* max steps, command timeout, auto-approve and full access.
|
| 63 |
+
- **Chat history.** Every chat is saved in the sidebar. Reopen one and GRAFT remembers it.
|
| 64 |
+
- **Copy, Regenerate and a context gauge** that shows how much of GRAFT's memory the chat uses.
|
| 65 |
+
- **Speculative decoding.** If a matching draft model (`GRAFT-10B-A2B-draft-*.gguf`) sits next to GRAFT-10B-A2B,
|
| 66 |
+
GRAFT Code uses it automatically for faster replies.
|
| 67 |
+
- **Use the model from other apps.** While GRAFT Code is open, its model is also an OpenAI-compatible API at
|
| 68 |
+
`http://127.0.0.1:1337/v1`. Other apps get their own slot, so they never slow GRAFT down by wiping its cache.
|
| 69 |
+
- **Web search and page reading.** Just ask. It uses DuckDuckGo with Wikipedia as a backup, and no API key is needed.
|
| 70 |
+
- **GRAFT.md notes.** Facts about your PC and how you like things done, read at the start of every chat.
|
| 71 |
+
|
| 72 |
+
## Talk to Claude (optional)
|
| 73 |
+
|
| 74 |
+
Copy the `graft-bridge` folder to `~/.claude/skills/`. In a Claude Code window, type `/graft-bridge`. Then tell GRAFT
|
| 75 |
+
*"ask Claude …"*: the question goes to that Claude Code window, and Claude's answer comes back to GRAFT.
|
| 76 |
+
|
| 77 |
+
## Where things are stored
|
| 78 |
+
|
| 79 |
+
Everything stays in `%LOCALAPPDATA%\GRAFTCode`: settings (`config.json`), personas, saved chats, your `GRAFT.md` notes
|
| 80 |
+
and the saved instruction cache.
|
| 81 |
+
|
| 82 |
+
## Good to know
|
| 83 |
+
|
| 84 |
+
- GRAFT Code runs real commands on your computer. Keep approvals on unless you trust the task.
|
| 85 |
+
- Only run one big model at a time. Two copies of a 10B model won't fit in 16 GB of RAM.
|
| 86 |
+
- Small models can make mistakes or make up results. Check anything important.
|
| 87 |
+
|
| 88 |
+
GRAFT Code bundles [llama.cpp](https://github.com/ggml-org/llama.cpp) (MIT).
|
graft-bridge/SKILL.md
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
name: graft-bridge
|
| 3 |
+
description: Listen for questions from GRAFT Code's talk_to_claude tool (GRAFT-1B running locally) and answer them from this Claude Code session. Use when the user types /graft-bridge or asks Claude to listen for / answer GRAFT.
|
| 4 |
+
---
|
| 5 |
+
|
| 6 |
+
# GRAFT bridge — answer GRAFT-1B from this Claude Code session
|
| 7 |
+
|
| 8 |
+
GRAFT Code (a local GRAFT-1B agent) has a `talk_to_claude` tool. It drops questions into
|
| 9 |
+
`%LOCALAPPDATA%\GRAFTCode\bridge\` and waits (up to 10 min) for an answer file. `graft_bridge.py` lives in this
|
| 10 |
+
skill's folder (`~/.claude/skills/graft-bridge/graft_bridge.py`).
|
| 11 |
+
|
| 12 |
+
## Start listening
|
| 13 |
+
Run with the Bash tool and `run_in_background: true` (keeps a heartbeat so GRAFT Code knows you're there; exits as
|
| 14 |
+
soon as a question arrives, which wakes you up):
|
| 15 |
+
|
| 16 |
+
```
|
| 17 |
+
python ~/.claude/skills/graft-bridge/graft_bridge.py wait
|
| 18 |
+
```
|
| 19 |
+
|
| 20 |
+
Tell the user in one line that the bridge is on.
|
| 21 |
+
|
| 22 |
+
## When it wakes you (background task completed)
|
| 23 |
+
1. Read the task output: `GRAFT QUESTION` + JSON with `id`, `message` (what GRAFT asks), `user_request` (what the
|
| 24 |
+
user typed to GRAFT — answer THIS if GRAFT garbled the question), and `cwd`.
|
| 25 |
+
2. Answer for a 1B model: short, plain, concrete (exact commands / code it can copy), under ~150 words unless code is
|
| 26 |
+
needed. GRAFT runs Windows PowerShell commands in `cwd`.
|
| 27 |
+
3. Send it (text on stdin):
|
| 28 |
+
```
|
| 29 |
+
python ~/.claude/skills/graft-bridge/graft_bridge.py answer <id> <<'EOF'
|
| 30 |
+
...answer...
|
| 31 |
+
EOF
|
| 32 |
+
```
|
| 33 |
+
4. Show the user the question and your answer briefly, then restart the listener (step "Start listening").
|
| 34 |
+
|
| 35 |
+
## Stop
|
| 36 |
+
`python ~/.claude/skills/graft-bridge/graft_bridge.py stop` and stop the background task.
|
graft-bridge/graft_bridge.py
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
r"""
|
| 2 |
+
GRAFT <-> Claude Code bridge (the Claude side). GRAFT Code's talk_to_claude tool drops questions in
|
| 3 |
+
%LOCALAPPDATA%\GRAFTCode\bridge\ ; a Claude Code session runs `wait` in the background, gets woken when a question
|
| 4 |
+
arrives, and replies with `answer`. By SmallAICreator / UltraLabs.
|
| 5 |
+
|
| 6 |
+
py graft_bridge.py wait block (with heartbeat) until a question is waiting, print it, exit
|
| 7 |
+
py graft_bridge.py answer <id> < text send the answer for question <id> (text on stdin)
|
| 8 |
+
py graft_bridge.py pending list unanswered questions
|
| 9 |
+
py graft_bridge.py stop mark the listener as gone
|
| 10 |
+
"""
|
| 11 |
+
import json, os, sys, time
|
| 12 |
+
from pathlib import Path
|
| 13 |
+
|
| 14 |
+
BRIDGE = Path(os.environ.get("LOCALAPPDATA", str(Path.home()))) / "GRAFTCode" / "bridge"
|
| 15 |
+
LISTENER = BRIDGE / "listener.json"
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
def atomic_write(p, obj):
|
| 19 |
+
tmp = p.with_suffix(".tmp")
|
| 20 |
+
tmp.write_text(json.dumps(obj, ensure_ascii=False, indent=1), encoding="utf-8")
|
| 21 |
+
os.replace(tmp, p)
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
def pending():
|
| 25 |
+
out = []
|
| 26 |
+
for q in sorted(BRIDGE.glob("q_*.json"), key=lambda p: p.stat().st_mtime):
|
| 27 |
+
qid = q.stem[2:]
|
| 28 |
+
if not (BRIDGE / f"a_{qid}.json").exists():
|
| 29 |
+
try:
|
| 30 |
+
out.append(json.loads(q.read_text(encoding="utf-8")))
|
| 31 |
+
except Exception: # half-written: pick it up next poll
|
| 32 |
+
pass
|
| 33 |
+
return out
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
def beat(state):
|
| 37 |
+
atomic_write(LISTENER, {"state": state, "ts": time.time(), "pid": os.getpid()})
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
def main():
|
| 41 |
+
BRIDGE.mkdir(parents=True, exist_ok=True)
|
| 42 |
+
cmd = sys.argv[1] if len(sys.argv) > 1 else "wait"
|
| 43 |
+
if cmd == "wait":
|
| 44 |
+
while True:
|
| 45 |
+
qs = pending()
|
| 46 |
+
if qs:
|
| 47 |
+
beat("handling") # GRAFT Code treats this as "Claude is on it"
|
| 48 |
+
q = qs[0]
|
| 49 |
+
print("GRAFT QUESTION")
|
| 50 |
+
print(json.dumps(q, ensure_ascii=False, indent=1))
|
| 51 |
+
if len(qs) > 1:
|
| 52 |
+
print(f"({len(qs) - 1} more waiting)")
|
| 53 |
+
return
|
| 54 |
+
beat("listening")
|
| 55 |
+
time.sleep(1.0)
|
| 56 |
+
elif cmd == "answer":
|
| 57 |
+
qid = sys.argv[2]
|
| 58 |
+
text = sys.stdin.buffer.read().decode("utf-8", "replace").strip() # raw bytes: Windows stdin mangles emoji
|
| 59 |
+
if not (BRIDGE / f"q_{qid}.json").exists():
|
| 60 |
+
sys.exit(f"no question {qid}")
|
| 61 |
+
atomic_write(BRIDGE / f"a_{qid}.json", {"id": qid, "answer": text, "time": time.time()})
|
| 62 |
+
beat("listening")
|
| 63 |
+
print(f"answered {qid} ({len(text)} chars)")
|
| 64 |
+
elif cmd == "pending":
|
| 65 |
+
print(json.dumps(pending(), ensure_ascii=False, indent=1))
|
| 66 |
+
elif cmd == "stop":
|
| 67 |
+
LISTENER.unlink(missing_ok=True)
|
| 68 |
+
print("listener stopped")
|
| 69 |
+
else:
|
| 70 |
+
sys.exit(__doc__)
|
| 71 |
+
|
| 72 |
+
|
| 73 |
+
if __name__ == "__main__":
|
| 74 |
+
main()
|