habithive-code 0.2.3 → 0.3.0
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/CHANGELOG.md +94 -0
- package/README.md +88 -117
- package/bin/habithive.mjs +132 -10
- package/docs/COMPATIBILITY.md +158 -0
- package/docs/guide/01-getting-started.md +135 -0
- package/docs/guide/02-modes.md +105 -0
- package/docs/guide/03-model-tree.md +100 -0
- package/docs/guide/04-omniroute.md +98 -0
- package/docs/guide/05-spec-workflow.md +159 -0
- package/docs/guide/06-agents-skills-tools.md +97 -0
- package/docs/guide/07-updates-and-versions.md +115 -0
- package/docs/guide/08-files-and-safety.md +83 -0
- package/docs/guide/09-troubleshooting.md +117 -0
- package/docs/guide/README.md +32 -0
- package/openspec-schemas/intent-driven/schema.yaml +278 -0
- package/openspec-schemas/intent-driven/templates/adr.md +23 -0
- package/openspec-schemas/intent-driven/templates/design.md +19 -0
- package/openspec-schemas/intent-driven/templates/proposal.md +23 -0
- package/openspec-schemas/intent-driven/templates/spec.md +10 -0
- package/openspec-schemas/intent-driven/templates/tasks.md +9 -0
- package/package.json +8 -4
- package/src/config.js +211 -65
- package/src/core.js +70 -37
- package/src/jsonc.js +128 -0
- package/src/logo.js +1 -1
- package/src/omniroute.js +119 -0
- package/src/openspec.js +87 -0
- package/src/plugin.js +214 -78
- package/src/setup.js +173 -37
- package/src/spec.js +45 -0
- package/src/update.js +62 -0
- package/src/versions.js +71 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to habithive-code are recorded here. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
4
|
+
|
|
5
|
+
## [Unreleased]
|
|
6
|
+
|
|
7
|
+
## [0.3.0] - 2026-11-12
|
|
8
|
+
|
|
9
|
+
### Breaking
|
|
10
|
+
|
|
11
|
+
- The model tree moved from `/model` to `/hive-models` (alias `hivemodels`). Hive no longer registers `/model`.
|
|
12
|
+
- Node.js 20.19 or later is required (OpenSpec 1.5.0 minimum). Hive checks at start and explains how to upgrade.
|
|
13
|
+
- `--minimal` is accepted but ignored with a notice. Setup always installs everything: OpenSpec, graphify, skills, Playwright MCP and OmniRoute.
|
|
14
|
+
- Toolchain versions are pinned and move only with a Hive release: opencode 1.19.4, oh-my-openagent 4.19.1, OpenSpec 1.5.0. Setup moves your installs to these versions.
|
|
15
|
+
- opencode self-update is turned off (`autoupdate: false`). Use `habithive update`.
|
|
16
|
+
- OpenCode 2 is not supported. Do not run `opencode upgrade --major`; complex mode and the model tree do not work on OpenCode 2.
|
|
17
|
+
- Launching `habithive` no longer repairs or rewrites opencode config. Repair happens in `habithive setup` and `habithive update`.
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- `habithive update`: installs the latest `habithive-code` from npm, then re-applies the release pins and config steps. Your mode, model overrides, baseline and other config are kept. For a source checkout it prints `git pull` instructions instead.
|
|
22
|
+
- Update notice: the TUI plugin checks npm at most once every 24 hours (cached, silent offline) and shows a toast when a newer version exists.
|
|
23
|
+
- `hive` as a second name for the `habithive` command. Setup warns if another `hive` program is on PATH.
|
|
24
|
+
- `habithive spec init`: sets up a project with OpenSpec and the `intent-driven` schema. See the spec workflow chapter in `docs/guide/`.
|
|
25
|
+
- The `intent-driven` schema ships with the package and is installed to OpenSpec's user-level data directory by setup and update.
|
|
26
|
+
- OmniRoute provider with 38 `auto/*` models, installed by setup and update. No API key is shipped; add yours with `/connect`. `habithive --gateway <ip>` sets the gateway address for a developer.
|
|
27
|
+
- "This screen (live)" in `/hive-models`, which opens opencode's native model picker with no relaunch.
|
|
28
|
+
- OpenCode 2 detection in the launcher, `habithive status` and setup, with recovery steps for npm, brew and curl installs. When setup moves opencode to the tested version, it tells you to ignore opencode's `opencode upgrade --major` suggestion.
|
|
29
|
+
- A warning when a chosen model will be rerouted by oh-my-openagent (Sisyphus with older GPT models, Hephaestus with non-GPT models).
|
|
30
|
+
- `docs/COMPATIBILITY.md` (tested version set and findings), the user guide in `docs/guide/`, and this changelog.
|
|
31
|
+
- Tests for the config editor, versions, update, OmniRoute, OpenSpec, spec init and a package check that fails on any IPv4 address in published files.
|
|
32
|
+
|
|
33
|
+
### Changed
|
|
34
|
+
|
|
35
|
+
- Mode switching renames between two complete config file sets (`~/.habithive/modes/<mode>/`) instead of editing config. A launch in the active mode writes nothing. The mode is recorded in `~/.habithive/mode`.
|
|
36
|
+
- All setup and update writes go through a safe JSONC editor: comments and trailing commas are tolerated, strings are never altered, nothing is written if the file does not parse, a timestamped backup is made first, and unchanged files are not rewritten. A file Hive changes is saved as plain JSON: every setting is kept, comments and layout are not (the backup keeps them).
|
|
37
|
+
- Main-agent model changes are automatic: Hive writes config, relaunches into the same session, then moves the on-screen agent to the chosen model. If that cannot be confirmed, the native picker opens with the model at the top of Recent.
|
|
38
|
+
- Relaunch is safer: never while a permission or question prompt is open, works on older plugin APIs, does not quit an opencode that `habithive` did not start, and tells you when the quit did not happen.
|
|
39
|
+
- Menu labels show the configured model and mark changes that wait for a relaunch.
|
|
40
|
+
- "ALL" is now "Everything (agents + tiers)".
|
|
41
|
+
- Setup upgrades an existing graphify install; OpenSpec is held at the pin.
|
|
42
|
+
- OmniRoute entries are Hive-owned: name, adapter and the 38 models are reset on each setup or update. Models you added and any existing `apiKey` or base URL are kept.
|
|
43
|
+
- The published package contains no gateway address. OmniRoute is installed with the placeholder `http://localhost:20128/v1`.
|
|
44
|
+
|
|
45
|
+
### Fixed
|
|
46
|
+
|
|
47
|
+
- A trailing comma in `opencode.jsonc` no longer causes your config to be overwritten with a minimal file.
|
|
48
|
+
- Glob strings such as `"src/**/*.ts"` and URLs are no longer corrupted by comment stripping.
|
|
49
|
+
- Launching `habithive` and switching modes no longer rewrite your config, so they no longer remove your comments.
|
|
50
|
+
- Fresh install: with no `oh-my-openagent.json`, the baseline was empty and every model change was refused ("baseline not captured"). An empty baseline is now valid and is re-captured once the file gains content Hive did not write.
|
|
51
|
+
- "Main agent - this screen" never took effect, and "All main agents" skipped the agent on screen. Both are replaced by the automatic flow above.
|
|
52
|
+
- A misleading "run: habithive update" line on OpenCode 2, and `habithive setup` trying `opencode upgrade <pin>` against OpenCode 2.
|
|
53
|
+
- A user-edited copy of the `intent-driven` schema is backed up to `~/.habithive/schema-backups/`, not into OpenSpec's schemas folder, which lists every folder as a schema.
|
|
54
|
+
|
|
55
|
+
### Removed
|
|
56
|
+
|
|
57
|
+
- The "Quick" section of the model tree, including "Main agent - this screen".
|
|
58
|
+
- The `--minimal` install mode (see Breaking).
|
|
59
|
+
- The `/model` command registration (see Breaking).
|
|
60
|
+
|
|
61
|
+
### Upgrading from 0.2.x
|
|
62
|
+
|
|
63
|
+
`habithive update` does not exist in 0.2.4 or earlier. Upgrade once by hand, then use it from then on:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
npm i -g habithive-code@latest && habithive setup
|
|
67
|
+
habithive update # for every later release
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Check that `node --version` is 20.19 or later first. If you were on a newer opencode than 1.19.4, setup moves it to the pin. If you ran `opencode upgrade --major`, follow the recovery steps that `habithive status` prints.
|
|
71
|
+
|
|
72
|
+
Setup leaves you in complex mode. If you used simple mode, run `habithive simple` once afterwards; your simple-mode settings are kept.
|
|
73
|
+
|
|
74
|
+
Setup saves the opencode config files it changes (usually `~/.config/opencode/opencode.jsonc`) as plain JSON. Every setting is kept, but comments and layout are removed. The original is saved next to the file as `opencode.jsonc.habithive-bak-<timestamp>`. Hive keeps only the newest 5 backups per file, so copy out any comments you want soon after upgrading.
|
|
75
|
+
|
|
76
|
+
## [0.2.4] - 2026-08-20
|
|
77
|
+
|
|
78
|
+
See the previous README for details.
|
|
79
|
+
|
|
80
|
+
## [0.2.3] - 2026-08-20
|
|
81
|
+
|
|
82
|
+
See the previous README for details.
|
|
83
|
+
|
|
84
|
+
## [0.2.2] - 2026-08-20
|
|
85
|
+
|
|
86
|
+
See the previous README for details.
|
|
87
|
+
|
|
88
|
+
## [0.2.1] - 2026-08-20
|
|
89
|
+
|
|
90
|
+
See the previous README for details.
|
|
91
|
+
|
|
92
|
+
## [0.2.0] - 2026-08-20
|
|
93
|
+
|
|
94
|
+
First release on the public npm registry (published 2026-08-20). See the previous README for details.
|
package/README.md
CHANGED
|
@@ -1,158 +1,129 @@
|
|
|
1
|
-
# habithive-code
|
|
1
|
+
# habithive-code (Hive)
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
two things effortless:
|
|
3
|
+
Hive is a control layer for [opencode](https://github.com/sst/opencode). It gives you a one-command install, live switching between simple and complex mode, a model tree for the [oh-my-openagent](https://github.com/code-yeongyu/oh-my-openagent) agents, an OmniRoute provider, a spec workflow (OpenSpec with the `intent-driven` schema), and a few bundled skills and tools.
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
2. **A model tree** — set models across oh-my-opencode's many agents/categories from a single menu instead of hand-editing JSON.
|
|
5
|
+
Hive does not bundle opencode or oh-my-openagent. It installs them from their official sources under their own licenses. The command is `habithive`; `hive` is the same command.
|
|
8
6
|
|
|
9
|
-
|
|
10
|
-
> them from their official sources under their own licenses. All credit for those
|
|
11
|
-
> projects belongs to their authors (see [Credits](#credits)).
|
|
7
|
+
## Quick start
|
|
12
8
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
- **Node.js ≥ 18**
|
|
16
|
-
- Internet access to fetch `opencode` and `oh-my-openagent` on first `habithive setup` (nothing else is required up front)
|
|
17
|
-
|
|
18
|
-
## Install
|
|
9
|
+
Prerequisites: Node.js 20.19 or newer (for example `nvm install 22`) and internet access for the first setup.
|
|
19
10
|
|
|
20
11
|
```sh
|
|
21
12
|
npm i -g habithive-code
|
|
22
|
-
habithive setup
|
|
13
|
+
habithive setup # installs and wires everything
|
|
14
|
+
habithive # complex mode, new session
|
|
23
15
|
```
|
|
24
16
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
For development, or if you'd rather run from a checkout than the published package:
|
|
17
|
+
To use OmniRoute, enable it once with the gateway address your team gives you, then add your key inside opencode:
|
|
28
18
|
|
|
29
19
|
```sh
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
npm test # sanity check — expect "16 passed, 0 failed"
|
|
33
|
-
node bin/habithive.mjs status # smoke test without installing anything globally
|
|
34
|
-
npm i -g . # optional: makes `habithive` available globally from this checkout
|
|
35
|
-
habithive setup # wire opencode + oh-my-opencode + the habithive plugin
|
|
20
|
+
habithive --gateway <ip> # Hive adds http:// and port 20128
|
|
21
|
+
# in opencode: /connect -> OmniRoute -> paste your key
|
|
36
22
|
```
|
|
37
23
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
|
45
|
-
|
|
46
|
-
|
|
|
47
|
-
|
|
|
48
|
-
|
|
|
49
|
-
|
|
|
50
|
-
|
|
|
51
|
-
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
habithive
|
|
63
|
-
habithive
|
|
64
|
-
habithive
|
|
65
|
-
habithive
|
|
66
|
-
habithive
|
|
67
|
-
|
|
68
|
-
```
|
|
24
|
+
Full walk-through: [Getting started](docs/guide/01-getting-started.md).
|
|
25
|
+
|
|
26
|
+
## What you get
|
|
27
|
+
|
|
28
|
+
| Component | What it is | Where it comes from |
|
|
29
|
+
|-----------|------------|---------------------|
|
|
30
|
+
| opencode | The coding agent, pinned to the tested version | npm `opencode-ai` |
|
|
31
|
+
| oh-my-openagent | Agent harness that complex mode enables, pinned | npm `oh-my-openagent@4.19.1` |
|
|
32
|
+
| Hive TUI plugin | Wordmark, `/habithive` menu, model tree, mode switch | this package |
|
|
33
|
+
| Playwright MCP | Browser tools for agents (`@playwright/mcp`) | added to the opencode `mcp` config |
|
|
34
|
+
| OpenSpec + intent-driven schema | Spec CLI pinned at **1.5.0**, plus the team schema | npm `@fission-ai/openspec`; schema shipped here |
|
|
35
|
+
| graphify | Knowledge-graph CLI (`graphifyy`) | PyPI, via `uv` or `pip` |
|
|
36
|
+
| Bundled skills | `karpathy-guidelines` and `graphify` | `skills/` in this package |
|
|
37
|
+
| OmniRoute | Provider with 38 `auto/*` models | written into both modes' config |
|
|
38
|
+
|
|
39
|
+
OpenSpec is pinned at **1.5.0** because newer releases reject the `intent-driven` schema. The Node 20.19 minimum comes from OpenSpec. There is no minimal install: setup always installs everything.
|
|
40
|
+
|
|
41
|
+
## Commands
|
|
42
|
+
|
|
43
|
+
| Command | What it does |
|
|
44
|
+
|---------|--------------|
|
|
45
|
+
| `habithive` | Complex mode, new session |
|
|
46
|
+
| `habithive simple` / `habithive complex` | Launch in that mode |
|
|
47
|
+
| `habithive -c` | Continue the last session |
|
|
48
|
+
| `habithive -s <id>` | Continue a specific session |
|
|
49
|
+
| `habithive setup` | Install opencode, oh-my-openagent, the plugin, extras and skills |
|
|
50
|
+
| `habithive update` | Install the latest release from npm and re-apply its pins |
|
|
51
|
+
| `habithive status` | Show the current mode and any queued switch |
|
|
52
|
+
| `habithive --gateway <ip>` | Point OmniRoute at your team's gateway |
|
|
53
|
+
| `hive spec init [path]` | Set up OpenSpec with the `intent-driven` schema in a project |
|
|
69
54
|
|
|
70
55
|
Inside opencode:
|
|
71
56
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
57
|
+
| Slash command | What it does |
|
|
58
|
+
|---------------|--------------|
|
|
59
|
+
| `/habithive` | Menu: model tree, switch mode, reset |
|
|
60
|
+
| `/hive-models` (alias `/hivemodels`) | The model tree |
|
|
61
|
+
| `/habithive-mode` | Switch simple or complex and reopen the same session |
|
|
62
|
+
| `/models` | opencode's own model picker |
|
|
63
|
+
| `/connect` | Add a provider key (OmniRoute, others) |
|
|
75
64
|
|
|
76
|
-
|
|
77
|
-
`▸ habithive` line on start and on exit (with the exact `habithive -c` / `habithive -s <id>`
|
|
78
|
-
resume commands).
|
|
65
|
+
## Guide
|
|
79
66
|
|
|
80
|
-
|
|
67
|
+
The guide lives in [docs/guide](docs/guide/README.md).
|
|
81
68
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
oracle · explore · librarian · multimodal-looker · metis · momus · sisyphus-junior
|
|
92
|
-
Categories (tiers) → oh-my-openagent.json
|
|
93
|
-
ultrabrain · deep · quick · visual-engineering · artistry · writing · unspecified-low · unspecified-high
|
|
94
|
-
```
|
|
69
|
+
1. [Getting started](docs/guide/01-getting-started.md): prerequisites, install, setup, first launch.
|
|
70
|
+
2. [Modes](docs/guide/02-modes.md): simple and complex mode, switching.
|
|
71
|
+
3. [Model tree](docs/guide/03-model-tree.md): setting models per agent and tier.
|
|
72
|
+
4. [OmniRoute](docs/guide/04-omniroute.md): provider, gateway, key.
|
|
73
|
+
5. [Spec workflow](docs/guide/05-spec-workflow.md): OpenSpec and the `intent-driven` schema.
|
|
74
|
+
6. [Agents, skills and tools](docs/guide/06-agents-skills-tools.md): what is bundled.
|
|
75
|
+
7. [Updates and versions](docs/guide/07-updates-and-versions.md): `habithive update`, pins.
|
|
76
|
+
8. [Files and safety](docs/guide/08-files-and-safety.md): every file Hive touches, guarantees, uninstall.
|
|
77
|
+
9. [Troubleshooting](docs/guide/09-troubleshooting.md): symptom, cause, fix.
|
|
95
78
|
|
|
96
|
-
|
|
97
|
-
row. Changes to subagents/tiers live in `oh-my-openagent.json` and apply on the next
|
|
98
|
-
launch (habithive reopens your session automatically after a change).
|
|
79
|
+
## Compatibility
|
|
99
80
|
|
|
100
|
-
|
|
81
|
+
Each release is tested against exactly one opencode and one oh-my-openagent version (defined in `src/versions.js`).
|
|
101
82
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
**Subagent/category models** = `oh-my-openagent.json`. habithive writes the right layer for
|
|
107
|
-
each level of the tree.
|
|
108
|
-
- Your original `oh-my-openagent.json` is snapshotted to `~/.habithive/baseline.json`
|
|
109
|
-
(with a byte-copy backup) so **Reset** restores it exactly.
|
|
83
|
+
| Component | Tested version |
|
|
84
|
+
|-----------|----------------|
|
|
85
|
+
| opencode (`OPENCODE_PIN`) | **1.19.4** |
|
|
86
|
+
| oh-my-openagent (`OMO_SPEC`) | **4.19.1** (5.x is not supported yet) |
|
|
110
87
|
|
|
111
|
-
|
|
88
|
+
`habithive setup` installs opencode 1.19.4, or moves an existing install to it with `opencode upgrade 1.19.4`.
|
|
112
89
|
|
|
113
|
-
|
|
114
|
-
opencode auto-updates its own binary; if a release changes internals, open an issue.
|
|
90
|
+
Setup also writes `"autoupdate": false` into `opencode.jsonc` in both modes' config sets, so opencode does not update itself past the tested version. Versions move only when a Hive release does: run `habithive update`. If the running opencode is not the tested version, the launcher and the in-app plugin each show a one-line warning and carry on.
|
|
115
91
|
|
|
116
|
-
|
|
92
|
+
Hive needs OpenCode 1. OpenCode 2 has a new plugin format and oh-my-openagent's agents do not run on it. **Do not run `opencode upgrade --major`** (opencode 1.19 suggests it). If OpenCode 2 is installed, the launcher, `habithive status` and `habithive setup` say so and show how to go back; setup never runs `opencode upgrade` against it. Use the command for how you installed OpenCode 2:
|
|
117
93
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
and you'll see two wordmarks stacked on the home screen. Fix it once with:
|
|
94
|
+
- npm: `npm rm -g @opencode/cli`, then `habithive setup`
|
|
95
|
+
- Homebrew: `brew uninstall opencode-v2`, then `habithive setup`
|
|
96
|
+
- curl installer: `curl -fsSL https://opencode.ai/install | bash -s -- --version 1.19.4`
|
|
122
97
|
|
|
123
|
-
|
|
124
|
-
npm rm -g maliv-code
|
|
125
|
-
rm -rf ~/.maliv
|
|
126
|
-
node -e "
|
|
127
|
-
const fs=require('fs'), os=require('os'), path=require('path');
|
|
128
|
-
const p=path.join(os.homedir(),'.config','opencode','tui.json');
|
|
129
|
-
const c=JSON.parse(fs.readFileSync(p,'utf8'));
|
|
130
|
-
c.plugin=(c.plugin||[]).filter(e=>!String(e).includes('maliv'));
|
|
131
|
-
fs.writeFileSync(p, JSON.stringify(c,null,2)+'\n');
|
|
132
|
-
console.log('cleaned:', p);
|
|
133
|
-
"
|
|
134
|
-
habithive setup
|
|
135
|
-
```
|
|
136
|
-
This is a one-time cleanup — anyone installing `habithive-code` fresh, who never had
|
|
137
|
-
the old package, can skip this section entirely.
|
|
98
|
+
Sessions created under OpenCode 2 may not open in OpenCode 1. Per-version findings are in [docs/COMPATIBILITY.md](docs/COMPATIBILITY.md).
|
|
138
99
|
|
|
139
100
|
## Uninstall
|
|
140
101
|
|
|
102
|
+
Switch to the mode whose config you want to keep first: deleting `~/.habithive` also deletes the other mode's stored config set.
|
|
103
|
+
|
|
141
104
|
```sh
|
|
142
|
-
habithive
|
|
105
|
+
habithive complex # switches back to complex mode; quit opencode when it opens
|
|
143
106
|
npm rm -g habithive-code
|
|
107
|
+
npm rm -g @fission-ai/openspec # optional
|
|
108
|
+
rm -rf ~/.local/share/openspec/schemas/intent-driven # optional
|
|
144
109
|
rm -rf ~/.habithive
|
|
145
110
|
```
|
|
146
|
-
|
|
147
|
-
and `opencode.jsonc
|
|
111
|
+
|
|
112
|
+
Then remove the Hive and oh-my-openagent entries from `~/.config/opencode/tui.json` and `opencode.jsonc`. Details: [Files and safety](docs/guide/08-files-and-safety.md#uninstall).
|
|
113
|
+
|
|
114
|
+
## Patch notes
|
|
115
|
+
|
|
116
|
+
See [CHANGELOG.md](CHANGELOG.md).
|
|
148
117
|
|
|
149
118
|
## Credits
|
|
150
119
|
|
|
151
|
-
|
|
120
|
+
Hive only orchestrates the work of others:
|
|
121
|
+
|
|
122
|
+
- [opencode](https://github.com/sst/opencode), the AI coding agent this runs on.
|
|
123
|
+
- [oh-my-openagent](https://github.com/code-yeongyu/oh-my-openagent) by code-yeongyu, the harness complex mode enables (Sustainable Use License 1.0).
|
|
152
124
|
|
|
153
|
-
|
|
154
|
-
- **[oh-my-opencode / oh-my-openagent](https://github.com/code-yeongyu/oh-my-openagent)** by **code-yeongyu** — the agent harness that "complex" mode enables (Sustainable Use License 1.0).
|
|
125
|
+
Bundled skills and their licenses: [THIRD-PARTY-NOTICES.md](THIRD-PARTY-NOTICES.md).
|
|
155
126
|
|
|
156
127
|
## License
|
|
157
128
|
|
|
158
|
-
habithive-code is [MIT](
|
|
129
|
+
habithive-code is [MIT](LICENSE). opencode and oh-my-openagent remain under their own licenses.
|
package/bin/habithive.mjs
CHANGED
|
@@ -7,21 +7,32 @@
|
|
|
7
7
|
// habithive -s <id> continue a specific session
|
|
8
8
|
// habithive new fresh session (default)
|
|
9
9
|
// habithive setup install opencode (if missing) + wire oh-my-opencode + habithive
|
|
10
|
+
// habithive update install the latest release from npm + reconcile the machine to it
|
|
11
|
+
// habithive spec init [path] scaffold OpenSpec (intent-driven) in a project
|
|
12
|
+
// habithive --gateway <ip> point OmniRoute at your team's gateway (IP only)
|
|
10
13
|
// habithive status show current mode + any queued in-app switch
|
|
14
|
+
// (hidden) habithive reconcile [--yes] setup minus the mode change; update runs it
|
|
11
15
|
//
|
|
12
16
|
// Default is a fresh session. In-app /habithive-mode queues a switch + quits; this
|
|
13
17
|
// loop relaunches in the queued mode with --continue (same session).
|
|
14
18
|
|
|
15
19
|
import { spawnSync } from "node:child_process";
|
|
16
|
-
import { readFileSync } from "node:fs";
|
|
17
|
-
import {
|
|
20
|
+
import { readFileSync, existsSync, realpathSync } from "node:fs";
|
|
21
|
+
import { join, resolve } from "node:path";
|
|
22
|
+
import { fileURLToPath } from "node:url";
|
|
23
|
+
import { currentMode, readMode, switchTo, recoverSwitch, isHabithive, PATHS } from "../src/config.js";
|
|
18
24
|
import { readNextMode, readNextSession, clearNextMode } from "../src/core.js";
|
|
19
|
-
import { runSetup } from "../src/setup.js";
|
|
25
|
+
import { runSetup, reconcile } from "../src/setup.js";
|
|
26
|
+
import { specInit } from "../src/spec.js";
|
|
27
|
+
import { gatewayURL, setGateway } from "../src/omniroute.js";
|
|
28
|
+
import { ConfigParseError } from "../src/jsonc.js";
|
|
29
|
+
import { checkLatest, installedVersion } from "../src/update.js";
|
|
30
|
+
import { NODE_MIN, OPENCODE_PIN, compareVersions, versionIn, isUnsupportedMajor, opencodeInstallMethod, v2Notice } from "../src/versions.js";
|
|
20
31
|
|
|
21
32
|
const C = {
|
|
22
33
|
reset: "\x1b[0m", dim: "\x1b[2m", bold: "\x1b[1m", cyan: "\x1b[36m",
|
|
23
34
|
violet: "\x1b[38;2;124;58;237m", // #7c3aed
|
|
24
|
-
white: "\x1b[97m",
|
|
35
|
+
white: "\x1b[97m", yellow: "\x1b[33m", red: "\x1b[31m",
|
|
25
36
|
};
|
|
26
37
|
let VERSION = "0.1.0";
|
|
27
38
|
try { VERSION = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8")).version || VERSION; } catch {}
|
|
@@ -38,13 +49,46 @@ function help() {
|
|
|
38
49
|
`habithive-code v${VERSION}\n\n` +
|
|
39
50
|
`usage: habithive [simple|complex] [-c | -s <id>] [project]\n` +
|
|
40
51
|
` habithive setup install opencode + oh-my-opencode + habithive\n` +
|
|
41
|
-
` habithive
|
|
52
|
+
` habithive update install the latest release + re-apply its pins\n` +
|
|
53
|
+
` habithive status show current mode\n` +
|
|
54
|
+
` habithive --gateway <ip> point OmniRoute at your team's gateway (IP only)\n` +
|
|
55
|
+
` hive spec init [path] scaffold OpenSpec (intent-driven) in a project\n\n` +
|
|
56
|
+
`hive and habithive are the same command.\n` +
|
|
42
57
|
`default: complex mode, new session. -c continue last, -s <id> continue specific.\n`,
|
|
43
58
|
);
|
|
44
59
|
}
|
|
45
60
|
|
|
46
61
|
function runOpencode(args) {
|
|
47
|
-
return spawnSync("opencode", args, { stdio: "inherit", shell: process.platform === "win32" }).status ?? 0;
|
|
62
|
+
return spawnSync("opencode", args, { stdio: "inherit", shell: process.platform === "win32", env: { ...process.env, HABITHIVE_LAUNCHER: "1" } }).status ?? 0;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Hint only: string-search the active tui.json (never parsed, never edited) for a
|
|
66
|
+
// Hive plugin file:// URL whose target is gone (e.g. the global npm install moved).
|
|
67
|
+
function staleHint() {
|
|
68
|
+
try {
|
|
69
|
+
for (const [u] of readFileSync(PATHS.TUI_JSON, "utf8").matchAll(/file:\/\/[^"'\s,\]]+/g)) {
|
|
70
|
+
if (!isHabithive(u) && !/habit-?hive/.test(u)) continue;
|
|
71
|
+
let p; try { p = fileURLToPath(u); } catch { continue; }
|
|
72
|
+
if (!existsSync(p)) { process.stdout.write(`${C.yellow}!${C.reset} habithive plugin path is stale — run: habithive setup\n`); return; }
|
|
73
|
+
}
|
|
74
|
+
} catch {}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const opencodeVersion = () => versionIn(spawnSync("opencode", ["--version"], { encoding: "utf8", shell: process.platform === "win32", timeout: 2000 }).stdout);
|
|
78
|
+
|
|
79
|
+
// Hint only, once per process (2 s cap, never blocks): warn when opencode is not the tested version.
|
|
80
|
+
// A different major (OpenCode 2, ADR-0010) gets the recovery notice instead; the launch continues.
|
|
81
|
+
let drift = false;
|
|
82
|
+
function driftHint() {
|
|
83
|
+
if (drift) return;
|
|
84
|
+
drift = true;
|
|
85
|
+
try {
|
|
86
|
+
const v = opencodeVersion();
|
|
87
|
+
if (isUnsupportedMajor(v)) {
|
|
88
|
+
const [head, ...steps] = v2Notice(v, opencodeInstallMethod()).split("\n");
|
|
89
|
+
process.stdout.write(`${C.red}! ${head}${C.reset}\n${steps.map((l) => `${C.dim}${l}${C.reset}`).join("\n")}\n`);
|
|
90
|
+
} else if (v && compareVersions(v, OPENCODE_PIN) !== 0) process.stdout.write(`${C.yellow}!${C.reset} opencode ${v} is not the tested ${OPENCODE_PIN} — run: habithive update\n`);
|
|
91
|
+
} catch {}
|
|
48
92
|
}
|
|
49
93
|
|
|
50
94
|
// Newest session id (the one just used) — `opencode session list` is newest-first.
|
|
@@ -56,14 +100,84 @@ function lastSessionId() {
|
|
|
56
100
|
} catch { return null; }
|
|
57
101
|
}
|
|
58
102
|
|
|
103
|
+
const real = (p) => { try { return realpathSync(p); } catch { return null; } };
|
|
104
|
+
const out = (m) => process.stdout.write(m + "\n");
|
|
105
|
+
|
|
106
|
+
// habithive update: npm install of the latest release, then the NEW version's reconcile (so its pins apply).
|
|
107
|
+
// A global install that links to a source checkout is never overwritten from npm.
|
|
108
|
+
async function update() {
|
|
109
|
+
const win = process.platform === "win32";
|
|
110
|
+
const pkgRoot = real(fileURLToPath(new URL("..", import.meta.url)));
|
|
111
|
+
const g = spawnSync("npm", ["root", "-g"], { encoding: "utf8", shell: win });
|
|
112
|
+
const gRoot = g.status === 0 ? real(g.stdout.trim()) : null; // realpath of the dir, not of the package: a link stays a link
|
|
113
|
+
const globalPkg = gRoot && join(gRoot, "habithive-code");
|
|
114
|
+
|
|
115
|
+
if (pkgRoot !== globalPkg) {
|
|
116
|
+
out(`${C.yellow}!${C.reset} running from a source checkout: ${pkgRoot}\n update it with: git -C ${pkgRoot} pull && habithive update`);
|
|
117
|
+
return reconcile({ yes: true });
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const latest = await checkLatest(), installed = installedVersion();
|
|
121
|
+
let now = installed;
|
|
122
|
+
if (latest && compareVersions(latest, installed) > 0) {
|
|
123
|
+
out(`${C.cyan}▸${C.reset} updating habithive-code ${installed} → ${latest}…`);
|
|
124
|
+
if (spawnSync("npm", ["i", "-g", "habithive-code@latest"], { stdio: "inherit", shell: win }).status !== 0) {
|
|
125
|
+
out(`${C.red}✗ npm install failed — fix npm's global prefix permissions (see README) and retry${C.reset}`);
|
|
126
|
+
return 1;
|
|
127
|
+
}
|
|
128
|
+
try { now = JSON.parse(readFileSync(join(globalPkg, "package.json"), "utf8")).version || latest; } catch { now = latest; }
|
|
129
|
+
} else if (latest === null) out(`${C.yellow}!${C.reset} could not reach the npm registry — reconciling the installed version`);
|
|
130
|
+
else out(`${C.cyan}▸${C.reset} habithive-code ${installed} is the latest`);
|
|
131
|
+
|
|
132
|
+
const r = spawnSync(process.execPath, [join(globalPkg, "bin", "habithive.mjs"), "reconcile", "--yes"], { stdio: "inherit" });
|
|
133
|
+
if (r.status !== 0) return r.status ?? 1;
|
|
134
|
+
out(`\nrestart opencode to load habithive v${now}`);
|
|
135
|
+
return 0;
|
|
136
|
+
}
|
|
137
|
+
|
|
59
138
|
async function main() {
|
|
60
|
-
|
|
139
|
+
if (compareVersions(process.versions.node, NODE_MIN) < 0) {
|
|
140
|
+
process.stderr.write(`habithive needs Node.js 20.19 or newer (running ${process.versions.node}) — upgrade, e.g. \`nvm install 22\`\n`);
|
|
141
|
+
process.exit(1);
|
|
142
|
+
}
|
|
143
|
+
let argv = process.argv.slice(2);
|
|
144
|
+
if (argv.some((a) => a === "--minimal" || a === "--min")) {
|
|
145
|
+
argv = argv.filter((a) => a !== "--minimal" && a !== "--min");
|
|
146
|
+
out("--minimal is no longer supported — installing everything");
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// --gateway <ip>: enable OmniRoute on this machine, then exit (never launches opencode)
|
|
150
|
+
const gi = argv.indexOf("--gateway");
|
|
151
|
+
if (gi >= 0) {
|
|
152
|
+
const val = argv[gi + 1];
|
|
153
|
+
if (!val || val.startsWith("-")) { process.stderr.write("usage: habithive --gateway <ip>\n"); process.exit(1); }
|
|
154
|
+
const g = gatewayURL(val);
|
|
155
|
+
if (!g.ok) { process.stderr.write(`${g.error}\n`); process.exit(1); }
|
|
156
|
+
try {
|
|
157
|
+
const { files } = setGateway(g.url);
|
|
158
|
+
out(`OmniRoute enabled (${g.url}) — add your key with /connect`);
|
|
159
|
+
out(`${C.dim}${files.length ? `updated: ${files.join(", ")}` : "already set"}${C.reset}`);
|
|
160
|
+
} catch (e) {
|
|
161
|
+
if (!(e instanceof ConfigParseError)) throw e;
|
|
162
|
+
process.stderr.write(`cannot read ${e.file}:${e.line}:${e.column}\n`);
|
|
163
|
+
process.exit(1);
|
|
164
|
+
}
|
|
165
|
+
process.exit(0);
|
|
166
|
+
}
|
|
61
167
|
|
|
62
168
|
// subcommands
|
|
63
|
-
if (argv[0] === "setup") { process.exit(await runSetup({ yes: argv.includes("--yes") || argv.includes("-y")
|
|
169
|
+
if (argv[0] === "setup") { process.exit(await runSetup({ yes: argv.includes("--yes") || argv.includes("-y") })); }
|
|
170
|
+
if (argv[0] === "reconcile") { process.exit(await reconcile({ yes: argv.includes("--yes") || argv.includes("-y") })); }
|
|
171
|
+
if (argv[0] === "spec") {
|
|
172
|
+
if (argv[1] === "init" && argv.length <= 3) process.exit(await specInit(argv[2] ? resolve(argv[2]) : process.cwd()));
|
|
173
|
+
out("usage: hive spec init [path]");
|
|
174
|
+
process.exit(1);
|
|
175
|
+
}
|
|
176
|
+
if (argv[0] === "update") { process.exit(await update()); }
|
|
64
177
|
if (argv[0] === "status") {
|
|
65
178
|
const q = readNextMode();
|
|
66
|
-
process.stdout.write(`habithive: ${currentMode()} mode${q ? ` · queued → ${q}` : ""}\n`);
|
|
179
|
+
process.stdout.write(`habithive: ${readMode() ?? currentMode()} mode${q ? ` · queued → ${q}` : ""}\n`);
|
|
180
|
+
try { const v = opencodeVersion(); if (isUnsupportedMajor(v)) out(`${C.red}!${C.reset} OpenCode ${v} is not supported — run habithive to see how to go back to OpenCode 1`); } catch {}
|
|
67
181
|
process.exit(0);
|
|
68
182
|
}
|
|
69
183
|
if (argv.includes("-h") || argv.includes("--help")) { help(); process.exit(0); }
|
|
@@ -82,10 +196,18 @@ async function main() {
|
|
|
82
196
|
else passthru.push(a);
|
|
83
197
|
}
|
|
84
198
|
|
|
199
|
+
const rec = recoverSwitch(); // finish/roll back an interrupted mode switch
|
|
200
|
+
if (rec?.notice) process.stdout.write(`${C.dim}${rec.notice}${C.reset}\n`);
|
|
201
|
+
staleHint();
|
|
85
202
|
clearNextMode();
|
|
86
203
|
// relaunch loop: honor an in-app queued mode switch, preserving the session
|
|
87
204
|
for (;;) {
|
|
88
|
-
|
|
205
|
+
if (mode !== currentMode()) { // a switch only renames files; a normal launch writes no config
|
|
206
|
+
const r = switchTo(mode);
|
|
207
|
+
r.notices.forEach((n) => process.stdout.write(`${C.dim}${n}${C.reset}\n`));
|
|
208
|
+
if (!r.ok) { process.stdout.write(`${C.yellow}habithive: ${r.error}${C.reset}\n`); mode = currentMode(); }
|
|
209
|
+
}
|
|
210
|
+
driftHint();
|
|
89
211
|
banner(mode, contArgs.length > 0);
|
|
90
212
|
runOpencode([...contArgs, ...passthru]);
|
|
91
213
|
const next = readNextMode();
|