@ataraxy-labs/weave
Advanced tools
| #!/usr/bin/env node | ||
| import { spawn } from 'node:child_process'; | ||
| import { existsSync } from 'node:fs'; | ||
| import { getInstalledBinaryPath } from '../scripts/package-meta.mjs'; | ||
| const binaryPath = getInstalledBinaryPath('weave-driver'); | ||
| if (!existsSync(binaryPath)) { | ||
| console.error( | ||
| 'weave-driver is not installed yet. Reinstall @ataraxy-labs/weave to download the binary.', | ||
| ); | ||
| process.exit(1); | ||
| } | ||
| const child = spawn(binaryPath, process.argv.slice(2), { | ||
| stdio: 'inherit', | ||
| }); | ||
| child.on('error', (error) => { | ||
| console.error(`Failed to launch weave-driver: ${error.message}`); | ||
| process.exit(1); | ||
| }); | ||
| child.on('exit', (code, signal) => { | ||
| if (signal) { | ||
| process.kill(process.pid, signal); | ||
| return; | ||
| } | ||
| process.exit(code ?? 1); | ||
| }); | ||
| for (const signal of ['SIGINT', 'SIGTERM', 'SIGHUP']) { | ||
| process.on(signal, () => { | ||
| if (!child.killed) { | ||
| child.kill(signal); | ||
| } | ||
| }); | ||
| } |
| #!/usr/bin/env node | ||
| import { spawn } from 'node:child_process'; | ||
| import { existsSync } from 'node:fs'; | ||
| import { getInstalledBinaryPath } from '../scripts/package-meta.mjs'; | ||
| const binaryPath = getInstalledBinaryPath('weave-mcp'); | ||
| if (!existsSync(binaryPath)) { | ||
| console.error( | ||
| 'weave-mcp is not installed yet. Reinstall @ataraxy-labs/weave to download the binary.', | ||
| ); | ||
| process.exit(1); | ||
| } | ||
| const child = spawn(binaryPath, process.argv.slice(2), { | ||
| stdio: 'inherit', | ||
| }); | ||
| child.on('error', (error) => { | ||
| console.error(`Failed to launch weave-mcp: ${error.message}`); | ||
| process.exit(1); | ||
| }); | ||
| child.on('exit', (code, signal) => { | ||
| if (signal) { | ||
| process.kill(process.pid, signal); | ||
| return; | ||
| } | ||
| process.exit(code ?? 1); | ||
| }); | ||
| for (const signal of ['SIGINT', 'SIGTERM', 'SIGHUP']) { | ||
| process.on(signal, () => { | ||
| if (!child.killed) { | ||
| child.kill(signal); | ||
| } | ||
| }); | ||
| } |
+39
| #!/usr/bin/env node | ||
| import { spawn } from 'node:child_process'; | ||
| import { existsSync } from 'node:fs'; | ||
| import { getInstalledBinaryPath } from '../scripts/package-meta.mjs'; | ||
| const binaryPath = getInstalledBinaryPath('weave'); | ||
| if (!existsSync(binaryPath)) { | ||
| console.error( | ||
| 'weave is not installed yet. Reinstall @ataraxy-labs/weave to download the binary.', | ||
| ); | ||
| process.exit(1); | ||
| } | ||
| const child = spawn(binaryPath, process.argv.slice(2), { | ||
| stdio: 'inherit', | ||
| }); | ||
| child.on('error', (error) => { | ||
| console.error(`Failed to launch weave: ${error.message}`); | ||
| process.exit(1); | ||
| }); | ||
| child.on('exit', (code, signal) => { | ||
| if (signal) { | ||
| process.kill(process.pid, signal); | ||
| return; | ||
| } | ||
| process.exit(code ?? 1); | ||
| }); | ||
| for (const signal of ['SIGINT', 'SIGTERM', 'SIGHUP']) { | ||
| process.on(signal, () => { | ||
| if (!child.killed) { | ||
| child.kill(signal); | ||
| } | ||
| }); | ||
| } |
+198
| # Changelog | ||
| This file starts at 0.4.0. For earlier releases see the | ||
| [GitHub releases page](https://github.com/Ataraxy-Labs/weave/releases). | ||
| Versions are shared across every crate in the workspace and the npm package, | ||
| so `weave-core`, `weave-crdt`, `weave-driver`, `weave-cli`, `weave-mcp`, | ||
| `weave-github` and `@ataraxy-labs/weave` all move together. | ||
| ## 0.5.0 | ||
| ### New — one line per merge | ||
| Set `WEAVE_EVENT=1` and the merge driver writes one JSON line per merge to | ||
| stderr, behind a `weave-event: ` prefix: | ||
| ```text | ||
| weave-event: {"schema":"weave-event","schema_version":"1.0.0","file":"src/app.py", | ||
| "outcome":"clean","exit_code":0,"confidence":"very_high","conflicts":0,"findings":0, | ||
| "entities":{...},"bytes_out":481,"ms_merge":4.43,"ms_total":5.72,...} | ||
| ``` | ||
| It answers the question a rebase raises — which files conflicted, on what, and | ||
| how long each took — in one pass over the lines instead of four stderr channels | ||
| joined by hand. A line is written for every outcome, including the ones that | ||
| produce nothing, because a channel that only records successes cannot explain a | ||
| bad run. Off by default, and everything on the line was already computed: | ||
| turning it on costs one line and no extra work. Fields are documented in | ||
| `crates/weave-mcp/schema/weave-event.schema.json`. | ||
| ### Breaking — library API | ||
| Nothing here affects the CLI, the merge driver or the MCP server. These change | ||
| Rust code that depends on `weave-core` or `weave-cli` directly. | ||
| **A merge is handed what it may touch, instead of reaching for it.** | ||
| `weave_core::host::Host` is new: a duplicate-name threshold and an optional | ||
| line-level merge, built at a program's entry point and passed down. Two things | ||
| inside the merge were not functions of their inputs — `WEAVE_MAX_DUPLICATES` | ||
| was read in the middle of the decision that used it, and the line-level route | ||
| spawned `git merge-file` with three temporary files. Both are worth having; | ||
| neither could be declined. | ||
| `entity_merge_fmt` and `entity_merge_with_registry` take a `&Host`, as does | ||
| `explain::explain`. `entity_merge` keeps its signature and runs against | ||
| `Host::default()`, which grants nothing — so the four-argument call is now a | ||
| function of its three inputs. Callers wanting the previous behavior pass: | ||
| ```rust | ||
| let host = weave_core::host::Host { | ||
| line_merge: Some(weave_core::host::git_line_merge), | ||
| ..Default::default() | ||
| }; | ||
| ``` | ||
| `WEAVE_MAX_DUPLICATES` still works: `weave-driver` reads it and puts it on the | ||
| host. It is documented in `weave-driver --help` for the first time. | ||
| **`weave_core::git` returns a typed error.** All six functions returned | ||
| `Box<dyn std::error::Error>` built from `format!`, so "git is not installed", | ||
| "this is not a repository", "these two refs share no history" and "git | ||
| declined" were one type. They are now `GitError::{NotRunnable, NotARepository, | ||
| NoMergeBase, Refused}`, each carrying its operands. Code using `?` into | ||
| `Box<dyn Error>` still compiles. | ||
| **`weave_core::stats` takes the path it reads and writes.** `load()` and | ||
| `save()` derived `~/.weave/stats.json` from the environment themselves. | ||
| `load(&Path)` and `save(&Path) -> bool` take it; `stats::default_path(home)` | ||
| offers the conventional location to a caller that wants it. `save` reports | ||
| whether the write landed instead of swallowing it. | ||
| **`weave_cli::patch` types its two boundaries.** `PatchOp::op` is now the `Op` | ||
| enum — the schema's own alphabet — rather than a `String` that let an | ||
| unrecognised verb decode and be silently ignored. `patch::apply` returns | ||
| `PatchError` instead of a sentence with two hashes in it, and | ||
| `patch::parse_ops_doc` is the only route from bytes to an `OpsDoc`, refusing | ||
| unknown fields and unknown majors by name. | ||
| ### Stricter — documents that did not match their own schemas | ||
| Both published schemas declare `additionalProperties: false`; no decoder | ||
| enforced it. Now they do. An ops document with a misspelled field, or an MCP | ||
| tool call with a misspelled argument, is refused instead of silently accepted | ||
| with the field discarded. A `weave_check` call with nothing but unrecognised | ||
| keys used to answer confidently about revisions the caller never named. | ||
| ### Fixed | ||
| - The MCP server answers `invalid_params` for a caller's own mistake — an | ||
| unknown entity name, an unreadable path, no repository — instead of | ||
| `internal_error` for everything, which told an agent "stop asking" when the | ||
| right answer was "ask differently". | ||
| - The GitHub webhook decodes the event payload into a type. A missing field | ||
| and a hostile one used to produce the same empty string, and the request | ||
| returned 200 having read nothing. | ||
| ## 0.4.0 | ||
| ### Breaking — library API | ||
| If you use weave as a CLI, a git merge driver, or through the MCP server, | ||
| nothing here affects you. These changes affect Rust code that depends on the | ||
| `weave-core` or `weave-crdt` crates directly. | ||
| The two library crates published a much larger surface than they supported. | ||
| Most of it was reachable by accident rather than on purpose, and some of it | ||
| had two spellings for the same function. This release cuts the surface down to | ||
| what is actually meant to be called, which breaks code that reached past it. | ||
| **`weave-crdt`: the module paths are gone.** | ||
| Every module (`content`, `error`, `merge`, `ops`, `state`, `sync`) is now | ||
| private to the crate. The `pub use` list in `lib.rs` is the entire public | ||
| surface. Previously each item had two paths — `weave_crdt::update_entity_content` | ||
| and `weave_crdt::content::update_entity_content` reached the same function — | ||
| and the two were free to drift apart without any caller noticing. | ||
| Migration: drop the module segment. `weave_crdt::sync::reconstruct_file_from_crdt` | ||
| becomes `weave_crdt::reconstruct_file_from_crdt`. Everything that was reachable | ||
| through a module path and is still supported is re-exported from the crate | ||
| root under the same name. | ||
| **`weave-crdt::record_modification` is removed.** It was a strictly weaker | ||
| duplicate of `update_entity_content`: same vector-clock increment, same three | ||
| summary writes, but it never wrote the `writes` register entry, so it could | ||
| leave `content_hash` naming a write no replica could join. Use | ||
| `update_entity_content`, which takes the content alongside the hash. | ||
| **`weave-crdt::MergeState` is no longer exported.** It was part of no supported | ||
| flow. `CrdtMergeResult` and `VersionVector` are unchanged. | ||
| **`weave-core::reconstruct` is removed.** The v1 reconstruct path it belonged | ||
| to no longer exists; the v2 pipeline does this work internally. | ||
| **`ResolutionStrategy::Fallback` is removed.** The variant was unconstructible — | ||
| `resolve` never emitted it, and a line-level fallback returns an empty audit | ||
| trail instead. If you matched on it exhaustively, that arm was dead. What is | ||
| true and now stated on `Op.fallback`: a fallback merge produces no ops at all, | ||
| so the read document is silent rather than flagged. | ||
| ### Added — library API | ||
| - `weave-crdt`: `anchor_of`, `ordered_entity_ids` and `Anchor`, so a caller can | ||
| read an entity's layout coordinate rather than infer it from the order. | ||
| - `weave-crdt`: `join`, `apply_op`, `value_of`, `EntityOp`, `EntityValue` and | ||
| `Write` — the join door, replacing ad-hoc detection. | ||
| - `weave-core`: the `binding`, `diagnose`, `explain`, `frame` and `v2` modules | ||
| are public. | ||
| ### Added — languages | ||
| `weave setup` now writes `merge=weave` lines for 17 more extensions: | ||
| .kt .tf .hcl .ml .mli .zig .elm .clj .edn .d | ||
| .lua .fish .nix .sql .tex .pl .csv | ||
| That is 38 languages and formats in total. The engine could already parse | ||
| these; `setup` simply had never claimed them, so git was handling them | ||
| line-by-line. | ||
| Each one earns its place by passing a five-scenario merge sweep | ||
| (`crates/weave-core/tests/language_coverage.rs`): two sides adding different | ||
| definitions merges clean, two sides rewriting the same definition conflicts, | ||
| a side that stood still is the identity, the same edit made twice lands once, | ||
| and nothing is dropped in any of them. | ||
| `.vue`, `.svelte`, `.erb` and `.hs` are **not** claimed, and the README no | ||
| longer says they are. weave can parse all four, but their entity model treats | ||
| a whole `<script>` block, template, or type signature as one unit, so two | ||
| people adding two different definitions conflict where they should merge, and | ||
| the conflict marker can land in the middle of a definition. Those files keep | ||
| getting git's line-level merge, which is the better answer until the parser | ||
| gains a real per-definition model for them. | ||
| ### Fixed | ||
| - **npm package was unusable.** `package.json` declared `weave`, | ||
| `weave-driver` and `weave-mcp` binaries, but `bin/` had been deleted from the | ||
| tree, so every install produced commands pointing at files that were not | ||
| there. All three wrappers are restored and the packed tarball is verified to | ||
| contain them. | ||
| - `package.json` had been sitting at 0.3.4 while the crates were at 0.3.6. | ||
| ### Changed | ||
| - Two merges that used to conflict now compose. When both sides edit inside one | ||
| method body, weave resolves at the statement and expression level instead of | ||
| handing back the whole method as a conflict — so one side adding a cache | ||
| lookup while the other renames a call in the same return statement produces | ||
| the composed result rather than a box. No edit is dropped either way; this | ||
| turns some conflicts into clean merges, never the reverse. | ||
| - The in-tree test suite is 401 tests, up from 268. Three test files that had | ||
| stopped shipping are back, and the language sweep is new. | ||
| - Documentation comments across the crates were reworded into plain | ||
| engineering terms. The rules they describe are enforced by tests, and the | ||
| comments now say that rather than borrowing a mathematical register for it. | ||
| No behaviour changed. |
+2
-1
| { | ||
| "name": "@ataraxy-labs/weave", | ||
| "version": "0.3.6", | ||
| "version": "0.5.0", | ||
| "description": "npm wrapper for the weave CLI, driver, and MCP server. Downloads matching release binaries and exposes weave, weave-driver, and weave-mcp commands.", | ||
@@ -31,2 +31,3 @@ "license": "MIT OR Apache-2.0", | ||
| "README.md", | ||
| "CHANGELOG.md", | ||
| "LICENSE-APACHE", | ||
@@ -33,0 +34,0 @@ "LICENSE-MIT" |
+109
-14
@@ -16,2 +16,3 @@ > **Part of the [Ataraxy Labs](https://ataraxy-labs.com) stack** — agent-native infrastructure for software development. See also: [sem](https://ataraxy-labs.com/sem) (semantic version control) · [inspect](https://github.com/Ataraxy-Labs/inspect) (semantic code review) · [opensessions](https://github.com/Ataraxy-Labs/opensessions) (tmux sidebar for coding agents). | ||
| <a href="#install">Install</a> · | ||
| <a href="#quickstart">Quickstart</a> · | ||
| <a href="#how-weave-fixes-this">How It Works</a> · | ||
@@ -26,6 +27,5 @@ <a href="#mcp-server">MCP Server</a> · | ||
| <img src="https://img.shields.io/badge/rust-stable-orange" alt="Rust"> | ||
| <img src="https://img.shields.io/badge/tests-124_passing-brightgreen" alt="Tests"> | ||
| <img src="https://img.shields.io/badge/version-0.3.0-blue" alt="Version"> | ||
| <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-yellow" alt="License"></a> | ||
| <img src="https://img.shields.io/badge/languages-28-blue" alt="Languages"> | ||
| <img src="https://img.shields.io/badge/tests-401_passing-brightgreen" alt="Tests"> | ||
| <a href="LICENSE-MIT"><img src="https://img.shields.io/badge/license-MIT_OR_Apache--2.0-yellow" alt="License"></a> | ||
| <img src="https://img.shields.io/badge/languages-38-blue" alt="Languages"> | ||
| </p> | ||
@@ -37,2 +37,14 @@ | ||
| ## Quickstart | ||
| ```bash | ||
| weave setup # this repo now merges through weave; git merge/rebase/cherry-pick unchanged | ||
| git merge <branch> # real conflicts land as markers with a `refused_by:` line stating why | ||
| weave explain <file> # per-hunk detail for one conflicted file, read off the actual git stages | ||
| # ...edit to resolve... | ||
| weave check # verify the working tree against the three merge stages; exits 1 on findings | ||
| ``` | ||
| See [Setup](#setup) for `--global`/`--local` variants and [MCP Server](#mcp-server) for agent-framework integration. | ||
| ## The Problem | ||
@@ -116,8 +128,21 @@ | ||
| ## Testing | ||
| weave's correctness is checked two ways. The open test suite in this | ||
| repository covers the documented merge properties and runs in CI. In | ||
| addition, every release is gated by a private conformance suite — currently | ||
| 2,800+ enumerated merge-rule cells and five corpora of real-world merges — | ||
| maintained separately, following the held-out-benchmark practice used by | ||
| conformance and evaluation suites elsewhere (SQLite/TH3, Khronos CTS, LLM | ||
| eval sets). PRs receive a pass/fail status from this suite automatically. | ||
| ## Conflict Markers | ||
| When a real conflict occurs, weave gives you context that Git doesn't: | ||
| When a real conflict occurs, weave gives you context that Git doesn't: which | ||
| entity, what type, and — on the line inside the box — which internal guard | ||
| declined to auto-merge and exactly which lines both sides disagree about. | ||
| ``` | ||
| <<<<<<< ours — function `process` (both modified) | ||
| <<<<<<< ours — function `process` (T, confidence: high) | ||
| // refused_by: statement_fold · collision: ` return data.upper()` +1 more | ||
| export function process(data: any) { | ||
@@ -130,11 +155,33 @@ return JSON.stringify(data); | ||
| } | ||
| >>>>>>> theirs — function `process` (both modified) | ||
| >>>>>>> theirs — function `process` (T, confidence: high) | ||
| ``` | ||
| You immediately know: what entity conflicted, what type it is, and why it conflicted. | ||
| Run `weave explain <file>` for more detail on every conflicted entity in the | ||
| file, and `weave check` after editing to verify your resolution against the | ||
| three merge stages — see [Quickstart](#quickstart). | ||
| ## Supported Languages | ||
| TypeScript, TSX, JavaScript, Python, Go, Rust, Java, C, C++, Ruby, C#, PHP, Swift, Kotlin, Elixir, Bash, HCL/Terraform, Fortran, Dart, Perl, OCaml, Scala, Zig, Vue, Svelte, XML, ERB, JSON, YAML, TOML, CSV, Markdown. Falls back to standard line-level merge for unsupported file types. | ||
| TypeScript, TSX, JavaScript, Python, Go, Rust, Java, C, C++, Ruby, C#, PHP, Swift, Kotlin, Scala, Dart, Elixir, Bash, Fish, Fortran, Perl, OCaml, Zig, Elm, Clojure, EDN, D, Lua, Nix, SQL, HCL/Terraform, LaTeX, XML, JSON, YAML, TOML, CSV, Markdown. Falls back to standard line-level merge for everything else. | ||
| `weave setup` writes a `merge=weave` line for exactly this list and nothing | ||
| else. Each language on it passes a five-scenario merge sweep — two sides adding | ||
| different definitions merges clean, two sides rewriting the same definition | ||
| conflicts, and nothing is dropped — in `crates/weave-core/tests/language_coverage.rs`. | ||
| Vue, Svelte, ERB and Haskell are parsed but deliberately **not** claimed. Their | ||
| entity model treats a whole `<script>` block, template, or type signature as a | ||
| single unit, so two people adding two different definitions conflict where they | ||
| should merge cleanly, and the conflict marker can land mid-definition. Those | ||
| files get Git's line-level merge instead, which is the better answer until the | ||
| parser gains a real per-definition model for them. | ||
| That is what the merge engine can parse. `weave setup` writes `.gitattributes` | ||
| rules for a narrower set, so Kotlin, HCL/Terraform, Vue, Svelte, ERB, CSV, Perl, | ||
| OCaml and Zig files still take git's line merge until you add the rule yourself: | ||
| ```bash | ||
| echo '*.kt merge=weave' >> .gitattributes # same shape for any extension above | ||
| ``` | ||
| ## Install | ||
@@ -146,3 +193,5 @@ | ||
| Or build from source (requires Rust): | ||
| Or build from source (requires Rust). Two binaries, both required: `weave` | ||
| (the CLI you run — `setup`/`explain`/`check`/...) and `weave-driver` (the one | ||
| git itself invokes on every merge; `weave setup` fails without it on `PATH`): | ||
@@ -152,6 +201,9 @@ ```bash | ||
| cd weave | ||
| cargo install --path crates/weave-cli | ||
| cargo install --path crates/weave-driver | ||
| cargo install --path crates/weave-cli # the `weave` binary | ||
| cargo install --path crates/weave-driver # the `weave-driver` binary git calls | ||
| ``` | ||
| Upgrading an existing source install? `cargo install` refuses to overwrite a | ||
| binary it didn't put there itself — add `--force` to either command above. | ||
| ## Setup | ||
@@ -179,2 +231,20 @@ | ||
| ### Global (every repo) | ||
| To make weave the default merge driver for **all** your repos at once (no per-repo setup, like [mergiraf](https://mergiraf.org/usage.html#registration-as-a-git-merge-driver)): | ||
| ```bash | ||
| weave setup --global | ||
| ``` | ||
| This writes the driver to your `~/.gitconfig` and the supported file-type rules to git's global attributes file (`~/.config/git/attributes`, or your `core.attributesfile` if set). No git repo required. Make sure `weave-driver` is on your `PATH` (it ships next to the `weave` binary). | ||
| The equivalent manual config, if you prefer: | ||
| ```bash | ||
| git config --global merge.weave.name "Entity-level semantic merge" | ||
| git config --global merge.weave.driver "weave-driver %O %A %B %L %P" | ||
| # then add `*.ts merge=weave` (etc.) to ~/.config/git/attributes | ||
| ``` | ||
| ## Jujutsu (jj) | ||
@@ -204,3 +274,3 @@ | ||
| ```bash | ||
| weave-cli preview feature-branch | ||
| weave preview feature-branch | ||
| ``` | ||
@@ -217,2 +287,25 @@ | ||
| After a real conflict, `weave explain <file>` and `weave check` are the | ||
| next two commands — see [Quickstart](#quickstart). | ||
| ## MCP Server | ||
| For agent frameworks that speak [MCP](https://modelcontextprotocol.io): | ||
| ```bash | ||
| # Claude Code | ||
| claude mcp add --scope user weave -- weave-mcp | ||
| # Any MCP client, via stdio (~/.config/claude/claude_desktop_config.json etc.) | ||
| { "mcpServers": { "weave": { "command": "weave-mcp" } } } | ||
| ``` | ||
| The server discovers the repo from the first tool call's file path, the | ||
| `WEAVE_REPO` env var, or its working directory. It exposes `weave_check` and | ||
| `weave_findings` as the read contract for acting on a merge, entity | ||
| inspection tools (`weave_extract_entities`, `weave_diff`, | ||
| `weave_get_dependencies`/`_dependents`), and a claim/release layer for | ||
| coordinating multiple agents in one repo. Each tool's own description states | ||
| when to call it and what an empty result means. | ||
| ## Architecture | ||
@@ -223,3 +316,5 @@ | ||
| weave-driver # Git merge driver binary (called by git via %O %A %B %L %P) | ||
| weave-cli # CLI: `weave setup` and `weave preview` | ||
| weave-cli # CLI: `weave setup`, `weave explain`, `weave check`, `weave preview`, ... | ||
| weave-crdt # Automerge-backed multi-agent coordination state | ||
| weave-mcp # MCP server exposing weave to agent frameworks | ||
| ``` | ||
@@ -226,0 +321,0 @@ |
Shell access
Supply chain riskThis module accesses the system shell. Accessing the system shell increases the risk of executing arbitrary code.
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
48714
54.8%12
50%354
37.21%333
39.92%11
37.5%6
100%