hordecode 0.0.0-stage → 0.1.1

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 ADDED
@@ -0,0 +1,23 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 MiMo Code, Xiaomi Corporation
4
+ Copyright (c) 2025 opencode
5
+ Copyright (c) 2026 Hordecode
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in all
15
+ copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,126 @@
1
- # Temporary Holding Version
1
+ <p align="center">
2
+ <a href="https://hordecode.com"><img src="https://raw.githubusercontent.com/siliconstencil/hordecode/main/assets/logo.svg" alt="Hordecode" width="96" height="96"></a>
3
+ </p>
4
+ <h1 align="center">Hordecode</h1>
5
+ <p align="center">The AI coding agent built for the terminal.</p>
6
+ <p align="center">
7
+ <a href="https://hordecode.com">Website</a> |
8
+ <a href="https://hordecode.com/docs/">Docs</a> |
9
+ <a href="https://github.com/siliconstencil/hordecode">GitHub</a>
10
+ </p>
11
+ <p align="center">
12
+ <a href="https://www.npmjs.com/package/hordecode"><img alt="npm" src="https://img.shields.io/npm/v/hordecode?style=flat-square" /></a>
13
+ <a href="https://github.com/siliconstencil/hordecode/actions/workflows/fork-typecheck.yml"><img alt="CI" src="https://img.shields.io/github/actions/workflow/status/siliconstencil/hordecode/fork-typecheck.yml?style=flat-square&branch=main" /></a>
14
+ <a href="https://github.com/siliconstencil/hordecode/blob/main/LICENSE"><img alt="MIT" src="https://img.shields.io/badge/license-MIT-blue?style=flat-square" /></a>
15
+ </p>
2
16
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
17
+ ---
18
+
19
+ Hordecode is a terminal-native AI coding agent. It is a friendly fork of
20
+ [opencode](https://github.com/anomalyco/opencode) that tracks upstream, carries a
21
+ delta-port of [MiMo Code](https://github.com/XiaomiMiMo/MiMo-Code), and adds its own
22
+ layer: a supervisor for long-running work, a harness pipeline, an inbox, persistent
23
+ memory with an optional on-device embedding index, skills, workflows and cron.
24
+
25
+ Every opencode provider, plugin and config file keeps working. Hordecode is not
26
+ affiliated with or endorsed by either upstream project.
27
+
28
+ ---
29
+
30
+ ## Quick Start
31
+
32
+ ```bash
33
+ # npm (all platforms)
34
+ npm install -g hordecode
35
+
36
+ # One-line install (macOS / Linux)
37
+ curl -fsSL https://raw.githubusercontent.com/siliconstencil/hordecode/main/install | bash
38
+
39
+ # Run
40
+ horde
41
+ ```
42
+
43
+ `hordecode` is an alias of `horde`; both commands are installed. On first launch, add a
44
+ provider with `horde providers` (or `horde auth`), then start a session.
45
+
46
+ Upgrade an existing install with the method it was installed with:
47
+
48
+ ```bash
49
+ horde upgrade # latest
50
+ horde upgrade 0.1.1 # a specific version
51
+ ```
52
+
53
+ > [!WARNING]
54
+ > 0.1.x is a pre-release. APIs, config and storage may change without a migration path.
55
+
56
+ <details>
57
+ <summary><strong>Install script details</strong></summary>
58
+
59
+ The script installs into `~/.hordecode/bin` and adds that directory to your shell
60
+ profile unless you pass `--no-modify-path`.
61
+
62
+ ```bash
63
+ curl -fsSL https://raw.githubusercontent.com/siliconstencil/hordecode/main/install | bash -s -- --version 0.1.1
64
+ curl -fsSL https://raw.githubusercontent.com/siliconstencil/hordecode/main/install | bash -s -- --no-modify-path
65
+ ./install --binary /path/to/hordecode # install a locally built binary
66
+ ```
67
+
68
+ Set `HORDECODE_RELEASE_BASE` to install from a mirror of the GitHub releases.
69
+
70
+ </details>
71
+
72
+ <details>
73
+ <summary><strong>Platforms</strong></summary>
74
+
75
+ | Target | npm package | Notes |
76
+ | --- | --- | --- |
77
+ | linux-x64 | `hordecode-linux-x64` | verified target; ships the on-device embedding index |
78
+ | linux-x64 (no AVX2) | `hordecode-linux-x64-baseline` | |
79
+ | linux-x64 musl | `hordecode-linux-x64-musl`, `-baseline-musl` | Alpine and other musl systems |
80
+ | linux-arm64 | `hordecode-linux-arm64`, `-musl` | |
81
+ | darwin-arm64 | `hordecode-darwin-arm64` | |
82
+ | darwin-x64 | `hordecode-darwin-x64`, `-baseline` | no embedding index (no onnxruntime binding) |
83
+ | windows-x64 | `hordecode-windows-x64`, `-baseline` | use npm; the install script needs bash |
84
+ | windows-arm64 | `hordecode-windows-arm64` | use npm |
85
+
86
+ The `hordecode` npm package selects the right platform package at install time. On
87
+ targets without the embedding index, memory search falls back to BM25.
88
+
89
+ </details>
90
+
91
+ ## What Hordecode adds
92
+
93
+ Fork features surface as tools the agent can call, not as extra CLI commands:
94
+
95
+ - **supervisor**: runs delegated work as supervised child sessions with spawn
96
+ references, task links and waiters, so multi-step work is tracked past one turn.
97
+ - **task** and **workflow**: units of work, and declared multi-step workflows.
98
+ - **memory-add / memory-recall / memory-update / memory-session / memory-status /
99
+ memory-dump / memory-gc**: persistent project memory. With `memory.embedding`
100
+ enabled (linux-x64) recall uses an on-device embedding index; otherwise BM25.
101
+ - **skill** and **skill-search**: discover and load skills from the user and project
102
+ skill directories; the bundled research pipeline ships as a skill set.
103
+ - **cron**: scheduled runs.
104
+ - **session**: manage sessions and worktrees, including a fleet view of running agents.
105
+
106
+ Behind the tools, the **inbox** carries messages between agents and sessions, and the
107
+ **harness** pipeline drives supervised runs.
108
+
109
+ The upstream tool set (read, edit, bash, grep, glob, lsp, MCP, plan, question, ...)
110
+ and the built-in **build**, **plan** and **general** agents are unchanged. Switch
111
+ agents with `Tab`.
112
+
113
+ ### Memory search: keyword by default, semantic opt-in
114
+
115
+ Memory recall is keyword search (BM25) out of the box and needs no download. Semantic
116
+ (vector) search is opt-in. Enable it in `hordecode.json`:
117
+
118
+ ```json
119
+ { "memory": { "embedding": true } }
120
+ ```
121
+
122
+ On first use Hordecode downloads the embedding model once (about 133 MB) into its cache
123
+ directory; recall stays keyword-only until the model is ready, then becomes hybrid
124
+ (keyword + semantic). The on-device index ships in the linux-x64 package; on other targets
125
+ the setting is accepted but recall stays keyword-only. `memory-status` reports the current
126
+ mode and vector count, and `horde debug embedding-check` verifies the pipeline.
package/bin/horde.exe ADDED
@@ -0,0 +1,10 @@
1
+ echo "Error: hordecode's postinstall script was not run." >&2
2
+ echo "" >&2
3
+ echo "This occurs when using --ignore-scripts during installation, or when using a" >&2
4
+ echo "package manager like pnpm that does not run postinstall scripts by default." >&2
5
+ echo "" >&2
6
+ echo "To fix this, run the postinstall script manually:" >&2
7
+ echo " cd node_modules/hordecode && node postinstall.mjs" >&2
8
+ echo "" >&2
9
+ echo "Or reinstall hordecode without the --ignore-scripts flag." >&2
10
+ exit 1
@@ -0,0 +1,10 @@
1
+ echo "Error: hordecode's postinstall script was not run." >&2
2
+ echo "" >&2
3
+ echo "This occurs when using --ignore-scripts during installation, or when using a" >&2
4
+ echo "package manager like pnpm that does not run postinstall scripts by default." >&2
5
+ echo "" >&2
6
+ echo "To fix this, run the postinstall script manually:" >&2
7
+ echo " cd node_modules/hordecode && node postinstall.mjs" >&2
8
+ echo "" >&2
9
+ echo "Or reinstall hordecode without the --ignore-scripts flag." >&2
10
+ exit 1
package/package.json CHANGED
@@ -1,6 +1,52 @@
1
1
  {
2
2
  "name": "hordecode",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "description": "Hordecode: AI coding agent for the terminal, a friendly fork of opencode with a MiMo Code delta-port",
4
+ "bin": {
5
+ "horde": "./bin/horde.exe",
6
+ "hordecode": "./bin/hordecode.exe"
7
+ },
8
+ "scripts": {
9
+ "postinstall": "node ./postinstall.mjs"
10
+ },
11
+ "version": "0.1.1",
12
+ "license": "MIT",
13
+ "homepage": "https://hordecode.com",
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "git+https://github.com/siliconstencil/hordecode.git"
17
+ },
18
+ "bugs": {
19
+ "url": "https://github.com/siliconstencil/hordecode/issues"
20
+ },
21
+ "keywords": [
22
+ "ai",
23
+ "agent",
24
+ "cli",
25
+ "coding-assistant",
26
+ "opencode",
27
+ "hordecode"
28
+ ],
29
+ "os": [
30
+ "darwin",
31
+ "linux",
32
+ "win32"
33
+ ],
34
+ "cpu": [
35
+ "arm64",
36
+ "x64"
37
+ ],
38
+ "optionalDependencies": {
39
+ "hordecode-linux-arm64": "0.1.1",
40
+ "hordecode-linux-x64": "0.1.1",
41
+ "hordecode-linux-x64-baseline": "0.1.1",
42
+ "hordecode-linux-arm64-musl": "0.1.1",
43
+ "hordecode-linux-x64-musl": "0.1.1",
44
+ "hordecode-linux-x64-baseline-musl": "0.1.1",
45
+ "hordecode-darwin-arm64": "0.1.1",
46
+ "hordecode-darwin-x64": "0.1.1",
47
+ "hordecode-darwin-x64-baseline": "0.1.1",
48
+ "hordecode-windows-arm64": "0.1.1",
49
+ "hordecode-windows-x64": "0.1.1",
50
+ "hordecode-windows-x64-baseline": "0.1.1"
51
+ }
6
52
  }
@@ -0,0 +1,221 @@
1
+ #!/usr/bin/env node
2
+
3
+ import childProcess from "child_process"
4
+ import fs from "fs"
5
+ import os from "os"
6
+ import path from "path"
7
+ import { createRequire } from "module"
8
+ import { fileURLToPath } from "url"
9
+
10
+ const __dirname = path.dirname(fileURLToPath(import.meta.url))
11
+ const require = createRequire(import.meta.url)
12
+ const packageJson = JSON.parse(fs.readFileSync(path.join(__dirname, "package.json"), "utf8"))
13
+
14
+ const platformMap = {
15
+ darwin: "darwin",
16
+ linux: "linux",
17
+ win32: "windows",
18
+ }
19
+ const archMap = {
20
+ x64: "x64",
21
+ arm64: "arm64",
22
+ arm: "arm",
23
+ }
24
+
25
+ const platform = platformMap[os.platform()] ?? os.platform()
26
+ const arch = archMap[os.arch()] ?? os.arch()
27
+ const base = `hordecode-${platform}-${arch}`
28
+ const sourceBinary = platform === "windows" ? "hordecode.exe" : "hordecode"
29
+ const legacySourceBinary = platform === "windows" ? "opencode.exe" : "opencode"
30
+ // The published package.json maps both commands to bin/<name>.exe on every platform
31
+ // (upstream postinstall.mjs:29 does the same), so the npm bin links resolve to the real binary.
32
+ const targetBinary = path.join(__dirname, "bin", "horde.exe")
33
+ const hordecodeTargetBinary = path.join(__dirname, "bin", "hordecode.exe")
34
+
35
+ function supportsAvx2() {
36
+ if (arch !== "x64") return false
37
+
38
+ if (platform === "linux") {
39
+ try {
40
+ return /(^|\s)avx2(\s|$)/i.test(fs.readFileSync("/proc/cpuinfo", "utf8"))
41
+ } catch {
42
+ return false
43
+ }
44
+ }
45
+
46
+ if (platform === "darwin") {
47
+ try {
48
+ const result = childProcess.spawnSync("sysctl", ["-n", "hw.optional.avx2_0"], {
49
+ encoding: "utf8",
50
+ timeout: 1500,
51
+ })
52
+ if (result.status !== 0) return false
53
+ return (result.stdout || "").trim() === "1"
54
+ } catch {
55
+ return false
56
+ }
57
+ }
58
+
59
+ if (platform === "windows") {
60
+ const command =
61
+ '(Add-Type -MemberDefinition "[DllImport(""kernel32.dll"")] public static extern bool IsProcessorFeaturePresent(int ProcessorFeature);" -Name Kernel32 -Namespace Win32 -PassThru)::IsProcessorFeaturePresent(40)'
62
+
63
+ for (const executable of ["powershell.exe", "pwsh.exe", "pwsh", "powershell"]) {
64
+ try {
65
+ const result = childProcess.spawnSync(executable, ["-NoProfile", "-NonInteractive", "-Command", command], {
66
+ encoding: "utf8",
67
+ timeout: 3000,
68
+ windowsHide: true,
69
+ })
70
+ if (result.status !== 0) continue
71
+ const output = (result.stdout || "").trim().toLowerCase()
72
+ if (output === "true" || output === "1") return true
73
+ if (output === "false" || output === "0") return false
74
+ } catch {
75
+ continue
76
+ }
77
+ }
78
+ }
79
+
80
+ return false
81
+ }
82
+
83
+ function isMusl() {
84
+ if (platform !== "linux") return false
85
+
86
+ try {
87
+ if (fs.existsSync("/etc/alpine-release")) return true
88
+ } catch {
89
+ // Ignore filesystem probes that are blocked by the host.
90
+ }
91
+
92
+ try {
93
+ const result = childProcess.spawnSync("ldd", ["--version"], { encoding: "utf8" })
94
+ return `${result.stdout || ""}${result.stderr || ""}`.toLowerCase().includes("musl")
95
+ } catch {
96
+ return false
97
+ }
98
+ }
99
+
100
+ function packageNames() {
101
+ const baseline = arch === "x64" && !supportsAvx2()
102
+
103
+ if (platform === "linux") {
104
+ if (isMusl()) {
105
+ if (arch === "x64")
106
+ return baseline
107
+ ? [`${base}-baseline-musl`, `${base}-musl`, `${base}-baseline`, base]
108
+ : [`${base}-musl`, `${base}-baseline-musl`, base, `${base}-baseline`]
109
+ return [`${base}-musl`, base]
110
+ }
111
+
112
+ if (arch === "x64")
113
+ return baseline
114
+ ? [`${base}-baseline`, base, `${base}-baseline-musl`, `${base}-musl`]
115
+ : [base, `${base}-baseline`, `${base}-musl`, `${base}-baseline-musl`]
116
+ return [base, `${base}-musl`]
117
+ }
118
+
119
+ if (arch === "x64") return baseline ? [`${base}-baseline`, base] : [base, `${base}-baseline`]
120
+ return [base]
121
+ }
122
+
123
+ function resolveBinary(name) {
124
+ const packageJsonPath = require.resolve(`${name}/package.json`)
125
+ let binaryPath = path.join(path.dirname(packageJsonPath), "bin", sourceBinary)
126
+ if (fs.existsSync(binaryPath)) return binaryPath
127
+ binaryPath = path.join(path.dirname(packageJsonPath), "bin", legacySourceBinary)
128
+ if (fs.existsSync(binaryPath)) return binaryPath
129
+ throw new Error(`Binary not found at ${binaryPath}`)
130
+ }
131
+
132
+ function installPackage(name) {
133
+ const version = packageJson.optionalDependencies?.[name]
134
+ if (!version) return
135
+
136
+ const temp = fs.mkdtempSync(path.join(os.tmpdir(), "hordecode-install-"))
137
+ try {
138
+ const result = childProcess.spawnSync(
139
+ "npm",
140
+ ["install", "--ignore-scripts", "--no-save", "--loglevel=error", "--prefix", temp, `${name}@${version}`],
141
+ { stdio: "inherit", windowsHide: true },
142
+ )
143
+ if (result.status !== 0) return
144
+ const packageDir = path.join(temp, "node_modules", name)
145
+ copyBinary(path.join(packageDir, "bin", sourceBinary), targetBinary)
146
+ copyRuntime(path.join(packageDir, "bin", sourceBinary), targetBinary)
147
+ return true
148
+ } finally {
149
+ fs.rmSync(temp, { recursive: true, force: true })
150
+ }
151
+ }
152
+
153
+ // The memory embedding index loads its onnxruntime binding and wasm assets from
154
+ // <exe-dir>/runtime and <exe-dir>/vendor (packages/memory/src/embedding/service.ts). The
155
+ // platform package ships them next to its binary; carry them next to the installed one.
156
+ function copyRuntime(source, target) {
157
+ const from = path.dirname(source)
158
+ const to = path.dirname(target)
159
+ for (const extra of ["runtime", "vendor"]) {
160
+ const dir = path.join(from, extra)
161
+ if (!fs.existsSync(dir)) continue
162
+ fs.rmSync(path.join(to, extra), { recursive: true, force: true })
163
+ fs.cpSync(dir, path.join(to, extra), { recursive: true })
164
+ }
165
+ }
166
+
167
+ function copyBinary(source, target) {
168
+ if (!fs.existsSync(source)) throw new Error(`Binary not found at ${source}`)
169
+ fs.mkdirSync(path.dirname(target), { recursive: true })
170
+ if (fs.existsSync(target)) fs.unlinkSync(target)
171
+ try {
172
+ fs.linkSync(source, target)
173
+ } catch {
174
+ fs.copyFileSync(source, target)
175
+ }
176
+ fs.chmodSync(target, 0o755)
177
+ }
178
+
179
+ function verifyBinary() {
180
+ const result = childProcess.spawnSync(targetBinary, ["--version"], {
181
+ encoding: "utf8",
182
+ stdio: "ignore",
183
+ windowsHide: true,
184
+ })
185
+ return result.status === 0
186
+ }
187
+
188
+ function main() {
189
+ for (const name of packageNames()) {
190
+ try {
191
+ const src = resolveBinary(name)
192
+ copyBinary(src, targetBinary)
193
+ copyRuntime(src, targetBinary)
194
+ try {
195
+ copyBinary(src, hordecodeTargetBinary)
196
+ } catch {}
197
+ if (verifyBinary()) return
198
+ } catch {
199
+ if (installPackage(name) && verifyBinary()) {
200
+ try {
201
+ const src2 = resolveBinary(name)
202
+ copyBinary(src2, hordecodeTargetBinary)
203
+ } catch {}
204
+ return
205
+ }
206
+ }
207
+ }
208
+
209
+ throw new Error(
210
+ `It seems your package manager failed to install the right hordecode CLI package. Try manually installing ${packageNames()
211
+ .map((name) => JSON.stringify(name))
212
+ .join(" or ")}.`,
213
+ )
214
+ }
215
+
216
+ try {
217
+ main()
218
+ } catch (error) {
219
+ console.error(error.message)
220
+ process.exit(1)
221
+ }