@einsia/agent-git 0.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +170 -0
- package/npm/README.md +75 -0
- package/npm/lib/log.js +12 -0
- package/npm/lib/platform.js +71 -0
- package/npm/lib/resolve.js +44 -0
- package/npm/lib/run.js +48 -0
- package/npm/postinstall.js +59 -0
- package/npm/shim.js +6 -0
- package/package.json +56 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Einsia
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# agit
|
|
2
|
+
|
|
3
|
+
[](LICENSE)
|
|
4
|
+
[](https://www.rust-lang.org)
|
|
5
|
+
|
|
6
|
+
Lossless version control for every agent session: publishable, resumable.
|
|
7
|
+
Works with Claude Code, Codex, OpenCode, and Cursor.
|
|
8
|
+
|
|
9
|
+
On disk, sessions are just JSONL files that get overwritten, compacted, and
|
|
10
|
+
cleaned up at any moment. `agit` puts snapshots and versions on top of them,
|
|
11
|
+
so "that conversation last Wednesday that finally cracked the bug" becomes
|
|
12
|
+
something you can find, continue, and hand to a teammate.
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
agit import <session> -n <agent> adopt an existing claude / codex / opencode / cursor session and record its first version
|
|
16
|
+
agit commit <agent> record another version (snapshot of the session's full current content)
|
|
17
|
+
agit push <agent> publish to the hub, get a link
|
|
18
|
+
agit clone <owner>/<agent> fetch (with git history) and pick up right away; --mine creates a copy under your name
|
|
19
|
+
agit upgrade upgrade the CLI itself to the latest release the hub announces
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Adopting and recording the first version are one command — the in-between
|
|
23
|
+
state ("linked, but unversioned") means nothing to anyone. To mark a session
|
|
24
|
+
without versioning it (e.g. offline), pass `--link-only`.
|
|
25
|
+
|
|
26
|
+
`agit clone` is **read-only by default**: nothing is created in your name,
|
|
27
|
+
`origin` points at the source, the session is installed into your runtime and
|
|
28
|
+
you can `agit commit` locally as usual. When you decide to take over, use
|
|
29
|
+
`--mine` — that creates your copy on the hub, repoints `origin` at it, and
|
|
30
|
+
remembers the source as `upstream`. Running `agit push` from a read-only
|
|
31
|
+
checkout offers exactly that.
|
|
32
|
+
|
|
33
|
+
The full command list is in `agit --help`.
|
|
34
|
+
|
|
35
|
+
## Install
|
|
36
|
+
|
|
37
|
+
### Users: npm
|
|
38
|
+
|
|
39
|
+
```sh
|
|
40
|
+
npx -y create-agit # one-shot: installs agit + wires skills/hooks/MCP
|
|
41
|
+
# or as a global package:
|
|
42
|
+
npm install -g @einsia/agent-git # pnpm add -g @einsia/agent-git works too
|
|
43
|
+
agit --version
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Both routes install a prebuilt binary — no Rust toolchain required. npm
|
|
47
|
+
picks the platform sub-package matching your `os`/`cpu`; on platforms with no
|
|
48
|
+
prebuilt binary (e.g. Windows) running `agit` prints the source-build recipe.
|
|
49
|
+
Details and environment variables: [`npm/README.md`](npm/README.md).
|
|
50
|
+
|
|
51
|
+
pnpm (v10+) does not run dependency install scripts by default, so the
|
|
52
|
+
automatic `agit setup` is skipped there — run `agit setup` once yourself
|
|
53
|
+
after installing.
|
|
54
|
+
|
|
55
|
+
The Linux artifact is **musl static-linked**, so no minimum glibc — the
|
|
56
|
+
release pipeline runs every Linux binary inside an Alpine / Amazon Linux /
|
|
57
|
+
Debian / Ubuntu container matrix before publishing.
|
|
58
|
+
|
|
59
|
+
You also need **git >= 2.28** (repo init uses `git init --initial-branch`).
|
|
60
|
+
Ubuntu 20.04 ships 2.25 — the installer prints a warning.
|
|
61
|
+
|
|
62
|
+
> Do not install `@einsia/agentgit` (no hyphen) — that is the pre-rewrite
|
|
63
|
+
> CLI; its protocol does not match this branch and it will not work.
|
|
64
|
+
|
|
65
|
+
### Contributors / eager users: from source
|
|
66
|
+
|
|
67
|
+
```sh
|
|
68
|
+
git clone https://github.com/Einsia/agent-git
|
|
69
|
+
cd agent-git
|
|
70
|
+
./setup.sh
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
`setup.sh` checks the toolchain (rustc, a C compiler), builds, and installs
|
|
74
|
+
the binary to `~/.local/bin/agit`. Common flags:
|
|
75
|
+
|
|
76
|
+
```sh
|
|
77
|
+
./setup.sh --debug # no LTO, much faster while hacking
|
|
78
|
+
./setup.sh --test # run cargo test --lib before installing
|
|
79
|
+
./setup.sh --help
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
To build a binary whose built-in hub is the staging deployment, set the build-time default
|
|
83
|
+
explicitly. Runtime `AGIT_HUB_URL` and `agit config set hub.url ...` still take precedence:
|
|
84
|
+
|
|
85
|
+
```sh
|
|
86
|
+
AGIT_DEFAULT_HUB_URL=https://staging.agent-git.com cargo build --release --locked
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Internal GitLab pipelines package commit-addressed `dev` and `staging` builds with this setting
|
|
90
|
+
embedded. Their `agit --version` output includes the channel and source commit, and `agit upgrade`
|
|
91
|
+
is disabled for them so an acceptance-test binary cannot silently turn into the public release.
|
|
92
|
+
Only an existing GitHub `agit-v*` tag produces the `prod` channel. The tag push runs the Release
|
|
93
|
+
workflow automatically; operators may also select the same reviewed tag through the workflow's
|
|
94
|
+
manual production input to retry a failed release without creating a different artifact identity.
|
|
95
|
+
|
|
96
|
+
Requires **rustc >= 1.88** and a C compiler (`rusqlite` uses the `bundled`
|
|
97
|
+
feature, so sqlite's C sources are compiled during the build). The reason for
|
|
98
|
+
the toolchain floor is in [`docs/01_setup.md`](docs/01_setup.md).
|
|
99
|
+
|
|
100
|
+
Both paths install the same binary. npm is for "users who don't want to touch
|
|
101
|
+
Rust"; `setup.sh` is for "people changing the code" and "people running
|
|
102
|
+
unreleased versions".
|
|
103
|
+
|
|
104
|
+
## Docs
|
|
105
|
+
|
|
106
|
+
| Goal | Where |
|
|
107
|
+
| --------------------------------------- | ------------------------------------------------------------- |
|
|
108
|
+
| Build, run the backend, sign in, debug | [`docs/01_setup.md`](docs/01_setup.md) |
|
|
109
|
+
| How sessions are stored locally | [`docs/02_session_store.md`](docs/02_session_store.md) |
|
|
110
|
+
| The terminal interface for humans | [`docs/07_tui.md`](docs/07_tui.md) |
|
|
111
|
+
| Login / token mechanics | [`docs/commands/auth.md`](docs/commands/auth.md) |
|
|
112
|
+
| Local low-entropy secret filtering | [`docs/05_global_secret_filter.md`](docs/05_global_secret_filter.md) |
|
|
113
|
+
| Reversible repository secret placeholders | [`docs/06_repository_secret_dictionary.md`](docs/06_repository_secret_dictionary.md) |
|
|
114
|
+
| Probed storage formats of each runtime | [`docs/mechanism-probing/`](docs/mechanism-probing/) |
|
|
115
|
+
| npm package behavior and env vars | [`npm/README.md`](npm/README.md) |
|
|
116
|
+
| Release artifact naming contract | [`.github/RELEASE_ARTIFACTS.md`](.github/RELEASE_ARTIFACTS.md) |
|
|
117
|
+
|
|
118
|
+
The server (**AgentGit**) is a separate repository, deployed on its own.
|
|
119
|
+
|
|
120
|
+
## Release
|
|
121
|
+
|
|
122
|
+
`Cargo.toml` is the single source of the version; every npm manifest must
|
|
123
|
+
agree with it (`node scripts/check-version.js` — run in CI and before
|
|
124
|
+
`npm pack`). To move the version, change every spot in one shot:
|
|
125
|
+
|
|
126
|
+
```sh
|
|
127
|
+
node scripts/bump-version.mjs 0.1.0 # Cargo.toml + Cargo.lock + all npm manifests
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Pushing the tag runs the whole chain — binaries first, npm right after:
|
|
131
|
+
|
|
132
|
+
```sh
|
|
133
|
+
git tag agit-v0.1.0 && git push origin agit-v0.1.0 # release.yml builds, smoke-tests, attaches artifacts
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Distribution to users runs through the npm registry: each target ships as a
|
|
137
|
+
platform sub-package (`@einsia/agent-git-linux-x64` and friends, gated by
|
|
138
|
+
`os`/`cpu` — npm installs only the matching one), and the main
|
|
139
|
+
`@einsia/agent-git` pulls them in via `optionalDependencies`. The GitHub
|
|
140
|
+
Release holds the canonical tarballs plus `SHA256SUMS`
|
|
141
|
+
([contract](.github/RELEASE_ARTIFACTS.md)).
|
|
142
|
+
|
|
143
|
+
Publishing to npm is the **npm publish** workflow. It runs automatically
|
|
144
|
+
once the Release workflow finishes green (and can be dispatched manually —
|
|
145
|
+
Actions → npm publish — for retries, `next` dist-tag trials, or a `dry_run`).
|
|
146
|
+
It publishes the whole family in dependency order — platform packages →
|
|
147
|
+
`@einsia/agent-git` → `create-agit` — with a real global install of the main
|
|
148
|
+
tarball as a preflight in between, authenticating via npm trusted publishing
|
|
149
|
+
(OIDC, no stored token). The equivalent local fallback:
|
|
150
|
+
|
|
151
|
+
```sh
|
|
152
|
+
gh release download agit-v0.1.0 -p 'agit-*.tar.gz' -D /tmp/agit-dist
|
|
153
|
+
node npm/publish.mjs /tmp/agit-dist # platform packages → main → create-agit
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
`npm/create-agit/` is the one-shot installer behind `npx -y create-agit` — it
|
|
157
|
+
depends on the main package and turns the npx cache copy into a durable
|
|
158
|
+
`~/.local/bin/agit`, then wires skills/hooks/MCP via `agit setup`. There is
|
|
159
|
+
deliberately no unscoped alias package: `agit` on npm belongs to an unrelated
|
|
160
|
+
project, and npm's typosquat rule blocks `agent-git` for being one punctuation
|
|
161
|
+
mark away from a third party's `agentgit`.
|
|
162
|
+
|
|
163
|
+
The version oracle (`GET /api/cli/version` on the hub) and `agit upgrade`
|
|
164
|
+
both read the npm registry; integrity verification uses the registry's
|
|
165
|
+
SRI (sha512). Self-hosted hubs that pin an internal fork set
|
|
166
|
+
`AGIT_BACKEND_CLI_REPO` and the whole chain reverts to the GitHub path.
|
|
167
|
+
|
|
168
|
+
`@einsia/agent-git` is a fresh package name with no existing users, so the
|
|
169
|
+
first release goes straight to `latest` — no need to hide in `next` first.
|
|
170
|
+
(The old `@einsia/agentgit` stays put; it points at the pre-rewrite CLI.)
|
package/npm/README.md
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# @einsia/agent-git (npm distribution)
|
|
2
|
+
|
|
3
|
+
This package ships the prebuilt **`agit`** CLI — no Rust toolchain needed.
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
npx -y create-agit # one-shot install: binary + skills/hooks/MCP
|
|
7
|
+
# or as a global package:
|
|
8
|
+
npm install -g @einsia/agent-git # pnpm add -g @einsia/agent-git — same CLI
|
|
9
|
+
agit --help
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Two package names, one CLI:
|
|
13
|
+
|
|
14
|
+
| Package | Role |
|
|
15
|
+
| ------------------- | ----------------------------------------------------------- |
|
|
16
|
+
| `@einsia/agent-git` | the real package — shim, postinstall, platform sub-packages |
|
|
17
|
+
| `create-agit` | `npx` one-shot installer, lands `agit` in `~/.local/bin` |
|
|
18
|
+
|
|
19
|
+
> The old package `@einsia/agentgit` (no hyphen) is the pre-rewrite CLI. This
|
|
20
|
+
> package is its successor, not a new version of it; there is no upgrade path
|
|
21
|
+
> between the two.
|
|
22
|
+
|
|
23
|
+
`agit` is a native Rust binary. This package is a thin wrapper: npm installs
|
|
24
|
+
the platform sub-package matching your `os`/`cpu` via `optionalDependencies`
|
|
25
|
+
(`@einsia/agent-git-linux-x64` and friends), the `agit` bin is a Node shim
|
|
26
|
+
that execs that binary with argv/stdio/exit-code forwarded, and `postinstall`
|
|
27
|
+
runs `agit setup` once to wire skills, hooks, MCP and AGENTS.md.
|
|
28
|
+
|
|
29
|
+
pnpm (v10+) blocks dependency install scripts unless approved, so under pnpm
|
|
30
|
+
the automatic `agit setup` does not run — run it once yourself after
|
|
31
|
+
installing (or `pnpm approve-builds -g` and reinstall).
|
|
32
|
+
|
|
33
|
+
The hub (**AgentGit**) is not in this package; it deploys separately.
|
|
34
|
+
|
|
35
|
+
## Platforms with prebuilt binaries
|
|
36
|
+
|
|
37
|
+
| OS | Arch | Platform package |
|
|
38
|
+
| ----- | ------------- | --------------------------------- |
|
|
39
|
+
| Linux | x86_64 | `@einsia/agent-git-linux-x64` |
|
|
40
|
+
| Linux | aarch64/arm64 | `@einsia/agent-git-linux-arm64` |
|
|
41
|
+
| macOS | x86_64 | `@einsia/agent-git-darwin-x64` |
|
|
42
|
+
| macOS | arm64 | `@einsia/agent-git-darwin-arm64` |
|
|
43
|
+
|
|
44
|
+
**Linux binaries are musl static-linked — no glibc floor.** Verified on Alpine,
|
|
45
|
+
Amazon Linux 2 (glibc 2.26), Debian 11/12, Ubuntu 20.04–24.04, x86_64 and
|
|
46
|
+
arm64. No more `libc.so.6: version 'GLIBC_2.xx' not found`.
|
|
47
|
+
|
|
48
|
+
On macOS, if Node runs under Rosetta (it reports itself as x64) the installer
|
|
49
|
+
detects it and installs the arm64 build.
|
|
50
|
+
|
|
51
|
+
Unsupported platforms (including Windows) get no prebuilt binary: running
|
|
52
|
+
`agit` prints build-from-source instructions. On WSL, the Linux binary works.
|
|
53
|
+
|
|
54
|
+
## Failure behavior
|
|
55
|
+
|
|
56
|
+
- Unsupported platform → the shim explains and exits 127 with the source-build
|
|
57
|
+
recipe.
|
|
58
|
+
- `agit setup` failing in postinstall never blocks the install — the CLI is on
|
|
59
|
+
disk; re-run `agit setup` any time.
|
|
60
|
+
|
|
61
|
+
## Environment variables
|
|
62
|
+
|
|
63
|
+
| Variable | Effect |
|
|
64
|
+
| ---------------- | ------------------------------------------------------------------- |
|
|
65
|
+
| `AGIT_BINARY` | Point at an existing `agit` binary; shim and postinstall honor it. |
|
|
66
|
+
| `AGIT_SKIP_SETUP`| Skip the postinstall `agit setup` (CI, managed environments). |
|
|
67
|
+
| `AGIT_FORCE_INSTALL` | Also run postinstall inside the source checkout (CI smoke tests). |
|
|
68
|
+
| `AGIT_HUB_URL` | Point the CLI at another hub (default `https://agent-git.com`). |
|
|
69
|
+
| `AGIT_NPM_REGISTRY` | Registry `agit upgrade` downloads platform packages from. |
|
|
70
|
+
|
|
71
|
+
## Upgrading
|
|
72
|
+
|
|
73
|
+
`agit upgrade` asks the hub for the latest CLI version and downloads the
|
|
74
|
+
platform package tarball straight from the npm registry, verified against the
|
|
75
|
+
registry's SRI (sha512) before anything is replaced.
|
package/npm/lib/log.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// All diagnostics go to stderr: the shim forwards the real binary's stdout, and
|
|
4
|
+
// the wrapper must not mix one word of its own into it. The prefix makes it
|
|
5
|
+
// obvious at a glance which line the wrapper said and which agit itself said.
|
|
6
|
+
const TAG = '[agit]';
|
|
7
|
+
|
|
8
|
+
module.exports = {
|
|
9
|
+
info: (m) => process.stderr.write(`${TAG} ${m}\n`),
|
|
10
|
+
warn: (m) => process.stderr.write(`${TAG} warning: ${m}\n`),
|
|
11
|
+
error: (m) => process.stderr.write(`${TAG} error: ${m}\n`),
|
|
12
|
+
};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { spawnSync } = require('child_process');
|
|
4
|
+
|
|
5
|
+
// The current Node runtime → platform key. The main package
|
|
6
|
+
// (optionalDependencies), the npx wrapper, publish.mjs and the CLI's
|
|
7
|
+
// `agit upgrade` share the same key set, and the key is the suffix of the npm
|
|
8
|
+
// platform package name: @einsia/agent-git-<key>. Changing this means changing
|
|
9
|
+
// release.yml's matrix, publish.mjs's mapping and upgrade.rs too.
|
|
10
|
+
//
|
|
11
|
+
// Linux is musl static linking: a gnu artifact built on ubuntu:24.04 locks in
|
|
12
|
+
// GLIBC_2.39 and nothing starts on older distributions; musl's static-pie has
|
|
13
|
+
// no interpreter, so it has no glibc floor. No win32: the release pipeline
|
|
14
|
+
// produces no windows artifacts.
|
|
15
|
+
const KEYS = {
|
|
16
|
+
'linux/x64': 'linux-x64',
|
|
17
|
+
'linux/arm64': 'linux-arm64',
|
|
18
|
+
'darwin/x64': 'darwin-x64',
|
|
19
|
+
'darwin/arm64': 'darwin-arm64',
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
// Platform key → Rust target triple (CI names artifact archives by triple).
|
|
23
|
+
const TRIPLES = {
|
|
24
|
+
'linux-x64': 'x86_64-unknown-linux-musl',
|
|
25
|
+
'linux-arm64': 'aarch64-unknown-linux-musl',
|
|
26
|
+
'darwin-x64': 'x86_64-apple-darwin',
|
|
27
|
+
'darwin-arm64': 'aarch64-apple-darwin',
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
function packageName(key) {
|
|
31
|
+
return `@einsia/agent-git-${key}`;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Under Rosetta, node reports itself as x64. sysctl.proc_translated=1 means
|
|
35
|
+
// the real hardware is arm64, so installing the arm64 package avoids the
|
|
36
|
+
// translation layer.
|
|
37
|
+
function realArch(nodePlatform, nodeArch) {
|
|
38
|
+
const platform = nodePlatform || process.platform;
|
|
39
|
+
const arch = nodeArch || process.arch;
|
|
40
|
+
if (platform !== 'darwin' || arch !== 'x64') return arch;
|
|
41
|
+
const r = spawnSync('sysctl', ['-n', 'sysctl.proc_translated'], { encoding: 'utf8' });
|
|
42
|
+
return !r.error && r.status === 0 && (r.stdout || '').trim() === '1' ? 'arm64' : arch;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Returns this machine's platform key; an unsupported platform (win32, for
|
|
46
|
+
// example) returns null and the caller picks the wording.
|
|
47
|
+
function packageKey(nodePlatform, nodeArch) {
|
|
48
|
+
const platform = nodePlatform || process.platform;
|
|
49
|
+
const arch = nodeArch === undefined ? realArch() : nodeArch;
|
|
50
|
+
return KEYS[`${platform}/${arch}`] || null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function binaryName(nodePlatform) {
|
|
54
|
+
return (nodePlatform || process.platform) === 'win32' ? 'agit.exe' : 'agit';
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function supportedList() {
|
|
58
|
+
return Object.values(KEYS)
|
|
59
|
+
.map((k) => `${k} (${TRIPLES[k]})`)
|
|
60
|
+
.join(', ');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
module.exports = {
|
|
64
|
+
KEYS,
|
|
65
|
+
TRIPLES,
|
|
66
|
+
packageName,
|
|
67
|
+
packageKey,
|
|
68
|
+
realArch,
|
|
69
|
+
binaryName,
|
|
70
|
+
supportedList,
|
|
71
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const platform = require('./platform');
|
|
6
|
+
|
|
7
|
+
// Where the real binary is. Distribution is a platform subpackage
|
|
8
|
+
// (@einsia/agent-git-<key>; npm installs only the one matching os/cpu), so the
|
|
9
|
+
// default meaning is "find that package in the dependency tree". The resolution
|
|
10
|
+
// order is deliberately fixed:
|
|
11
|
+
//
|
|
12
|
+
// 1. An explicit AGIT_BINARY override — the user is pointing the way and gets
|
|
13
|
+
// the last word.
|
|
14
|
+
// 2. This package's bin/<agit> — placed by hand outside the publish flow (a
|
|
15
|
+
// build a volunteer is testing, for example).
|
|
16
|
+
// 3. The platform subpackage's package/bin/<agit> — the normal path.
|
|
17
|
+
//
|
|
18
|
+
// A require that resolves nothing means npm skipped the optional dep for this
|
|
19
|
+
// platform (an unsupported system).
|
|
20
|
+
function resolveBinary(pkgRoot) {
|
|
21
|
+
const override = process.env.AGIT_BINARY;
|
|
22
|
+
if (override) {
|
|
23
|
+
if (!fs.existsSync(override)) {
|
|
24
|
+
throw new Error(`AGIT_BINARY points at "${override}", but there is no file there.`);
|
|
25
|
+
}
|
|
26
|
+
return override;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const local = path.join(pkgRoot, 'bin', platform.binaryName());
|
|
30
|
+
if (fs.existsSync(local)) return local;
|
|
31
|
+
|
|
32
|
+
const key = platform.packageKey();
|
|
33
|
+
if (!key) return null;
|
|
34
|
+
const name = path.join(platform.packageName(key), 'bin', platform.binaryName()).replace(/\\/g, '/');
|
|
35
|
+
// Resolve by walking node_modules up from the package root; that matches
|
|
36
|
+
// where npm installs the optional dep.
|
|
37
|
+
try {
|
|
38
|
+
return require.resolve(name, { paths: [pkgRoot] });
|
|
39
|
+
} catch {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
module.exports = { resolveBinary };
|
package/npm/lib/run.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { spawnSync } = require('child_process');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const platform = require('./platform');
|
|
6
|
+
const { resolveBinary } = require('./resolve');
|
|
7
|
+
const log = require('./log');
|
|
8
|
+
|
|
9
|
+
// Entry point for the `agit` command. Thin is deliberate: forward argv / stdio /
|
|
10
|
+
// exit code, nothing else. agit gets written into scripts and hooks, so exit
|
|
11
|
+
// codes and stdio pass-through are load-bearing semantics and must not change.
|
|
12
|
+
function run() {
|
|
13
|
+
const pkgRoot = path.join(__dirname, '..');
|
|
14
|
+
let bin = null;
|
|
15
|
+
try {
|
|
16
|
+
bin = resolveBinary(pkgRoot);
|
|
17
|
+
} catch (e) {
|
|
18
|
+
log.error(e.message);
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (!bin) {
|
|
23
|
+
// npm skipped the optional dep for this os/cpu = this platform has no
|
|
24
|
+
// prebuilt artifact (win32, freebsd, ...). This block is often everything
|
|
25
|
+
// the user sees, so on its own it has to say why there is no binary and
|
|
26
|
+
// which command fixes it.
|
|
27
|
+
log.error(`no prebuilt agit binary for ${process.platform}/${process.arch}.`);
|
|
28
|
+
log.error('');
|
|
29
|
+
log.error(`prebuilt: ${platform.supportedList()}`);
|
|
30
|
+
log.error('');
|
|
31
|
+
log.error('build from source instead:');
|
|
32
|
+
log.error(' git clone https://github.com/Einsia/agent-git');
|
|
33
|
+
log.error(' cd agent-git && ./setup.sh');
|
|
34
|
+
log.error('');
|
|
35
|
+
log.error('already have a binary? AGIT_BINARY=/path/to/agit agit …');
|
|
36
|
+
// 127 is the shell's conventional code for "command not found".
|
|
37
|
+
process.exit(127);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const r = spawnSync(bin, process.argv.slice(2), { stdio: 'inherit' });
|
|
41
|
+
if (r.error) {
|
|
42
|
+
log.error(`failed to start ${bin}: ${r.error.message}`);
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
process.exit(r.status === null ? 1 : r.status);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
module.exports = { run };
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// postinstall: the binary lands with the optional dep, so there is nothing to download. It
|
|
4
|
+
// does two things in passing, each idempotent and neither able to block the install when it
|
|
5
|
+
// fails:
|
|
6
|
+
//
|
|
7
|
+
// 1. Self-check that the binary really runs (an architecture mismatch, Rosetta and the like
|
|
8
|
+
// surface here rather than the first time the user types agit).
|
|
9
|
+
// 2. Run `agit setup` — the product promise of "installed by default at download time":
|
|
10
|
+
// skills, hooks, MCP and AGENTS.md, all in one go. AGIT_SKIP_SETUP=1 turns it off
|
|
11
|
+
// (CI / hosted environments).
|
|
12
|
+
//
|
|
13
|
+
// npm displays postinstall output badly, so stay quiet: speak only when something is wrong.
|
|
14
|
+
|
|
15
|
+
const { spawnSync } = require('child_process');
|
|
16
|
+
const path = require('path');
|
|
17
|
+
const fs = require('fs');
|
|
18
|
+
const { resolveBinary } = require('./lib/resolve');
|
|
19
|
+
const log = require('./lib/log');
|
|
20
|
+
|
|
21
|
+
function truthy(v) {
|
|
22
|
+
return v != null && v !== '' && v !== '0' && String(v).toLowerCase() !== 'false';
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function main() {
|
|
26
|
+
// Whoever runs `npm install` inside the source repository is not a user: a Cargo.toml next
|
|
27
|
+
// door and no node_modules in its own path = the contributor path, and contributors go through
|
|
28
|
+
// ./setup.sh. AGIT_FORCE_INSTALL=1 bypasses this (CI smoke test).
|
|
29
|
+
const pkgRoot = path.join(__dirname);
|
|
30
|
+
const inNodeModules = pkgRoot.split(path.sep).includes('node_modules');
|
|
31
|
+
if (!truthy(process.env.AGIT_FORCE_INSTALL) &&
|
|
32
|
+
!inNodeModules && fs.existsSync(path.join(pkgRoot, '..', 'Cargo.toml'))) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
let bin;
|
|
37
|
+
try {
|
|
38
|
+
bin = resolveBinary(pkgRoot);
|
|
39
|
+
} catch (e) {
|
|
40
|
+
log.warn(e.message);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
if (!bin) return; // unsupported platform: the shim gives full instructions on the next command.
|
|
44
|
+
|
|
45
|
+
const check = spawnSync(bin, ['--version'], { encoding: 'utf8' });
|
|
46
|
+
if (check.error || check.status !== 0) {
|
|
47
|
+
log.warn(`installed binary does not run: ${bin}`);
|
|
48
|
+
log.warn((check.stderr || check.error?.message || `exit ${check.status}`).trim());
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (truthy(process.env.AGIT_SKIP_SETUP)) return;
|
|
53
|
+
const setup = spawnSync(bin, ['setup'], { stdio: 'inherit' });
|
|
54
|
+
if (setup.status !== 0) {
|
|
55
|
+
log.warn('`agit setup` did not fully succeed — re-run it any time; the CLI itself is installed.');
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
main();
|
package/npm/shim.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@einsia/agent-git",
|
|
3
|
+
"version": "0.0.9",
|
|
4
|
+
"description": "Version control for agent sessions — the agit CLI plus skills/hooks/MCP wiring for Claude Code, Codex, OpenCode and Cursor. The AgentGit hub deploys separately.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"agit",
|
|
7
|
+
"agent",
|
|
8
|
+
"git",
|
|
9
|
+
"llm",
|
|
10
|
+
"claude",
|
|
11
|
+
"claude-code",
|
|
12
|
+
"codex",
|
|
13
|
+
"opencode",
|
|
14
|
+
"cursor",
|
|
15
|
+
"session",
|
|
16
|
+
"mcp",
|
|
17
|
+
"cli"
|
|
18
|
+
],
|
|
19
|
+
"license": "MIT",
|
|
20
|
+
"homepage": "https://agent-git.com",
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "git+https://github.com/Einsia/agent-git.git"
|
|
24
|
+
},
|
|
25
|
+
"bugs": {
|
|
26
|
+
"url": "https://github.com/Einsia/agent-git/issues"
|
|
27
|
+
},
|
|
28
|
+
"bin": {
|
|
29
|
+
"agit": "npm/shim.js"
|
|
30
|
+
},
|
|
31
|
+
"files": [
|
|
32
|
+
"npm/shim.js",
|
|
33
|
+
"npm/postinstall.js",
|
|
34
|
+
"npm/lib/",
|
|
35
|
+
"npm/README.md",
|
|
36
|
+
"README.md"
|
|
37
|
+
],
|
|
38
|
+
"scripts": {
|
|
39
|
+
"postinstall": "node npm/postinstall.js",
|
|
40
|
+
"check-version": "node scripts/check-version.js",
|
|
41
|
+
"bump-version": "node scripts/bump-version.mjs",
|
|
42
|
+
"prepack": "node scripts/check-version.js"
|
|
43
|
+
},
|
|
44
|
+
"engines": {
|
|
45
|
+
"node": ">=20"
|
|
46
|
+
},
|
|
47
|
+
"publishConfig": {
|
|
48
|
+
"access": "public"
|
|
49
|
+
},
|
|
50
|
+
"optionalDependencies": {
|
|
51
|
+
"@einsia/agent-git-linux-x64": "0.0.9",
|
|
52
|
+
"@einsia/agent-git-linux-arm64": "0.0.9",
|
|
53
|
+
"@einsia/agent-git-darwin-x64": "0.0.9",
|
|
54
|
+
"@einsia/agent-git-darwin-arm64": "0.0.9"
|
|
55
|
+
}
|
|
56
|
+
}
|