@cooked-ham/hamgoose 0.1.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/README.md +51 -0
- package/bin/hamgoose.mjs +152 -0
- package/package.json +39 -0
package/README.md
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# @cooked-ham/hamgoose
|
|
2
|
+
|
|
3
|
+
**npm launcher for [hamgoose](https://github.com/cooked-ham/hamgoose)** — Factory-Droid-style
|
|
4
|
+
Mission orchestration for [Goose](https://goose-docs.ai): goal in, structured plan,
|
|
5
|
+
approval gate, isolated workers in git worktrees, dual validation, auto-correction.
|
|
6
|
+
Out: validated code.
|
|
7
|
+
|
|
8
|
+
## What is this package?
|
|
9
|
+
|
|
10
|
+
hamgoose itself is a **Python** stdio MCP server — this package is a thin,
|
|
11
|
+
dependency-free launcher so the install works from the Node world too. It finds
|
|
12
|
+
(or installs) the Python package and runs it. All mission logic lives in the
|
|
13
|
+
[GitHub repo](https://github.com/cooked-ham/hamgoose) — single source of truth.
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
# install the Python package (idempotent; finds Python 3.11+, falls back to `py -3`)
|
|
19
|
+
npx @cooked-ham/hamgoose install
|
|
20
|
+
|
|
21
|
+
# install + register with Goose's config in one shot
|
|
22
|
+
npx @cooked-ham/hamgoose register
|
|
23
|
+
|
|
24
|
+
# run the MCP stdio server (what Goose spawns)
|
|
25
|
+
npx -y @cooked-ham/hamgoose
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Use it as a Goose extension
|
|
29
|
+
|
|
30
|
+
`goose configure` → **Extensions → Add Extension** → Type `STDIO`, Name
|
|
31
|
+
`hamgoose`, Command:
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
npx -y @cooked-ham/hamgoose
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
…or the equivalent `extensions:` entry in `config.yaml`. Then in any repo:
|
|
38
|
+
`goose` → `/start_mission`.
|
|
39
|
+
|
|
40
|
+
Equivalent channels: `pip install git+https://github.com/cooked-ham/hamgoose.git`
|
|
41
|
+
or, once on PyPI, `pip install hamgoose` / `uvx hamgoose`.
|
|
42
|
+
|
|
43
|
+
## Requirements
|
|
44
|
+
|
|
45
|
+
- Node ≥ 18
|
|
46
|
+
- Python ≥ 3.11 (auto-detected: `python3`, `python`, `py -3`)
|
|
47
|
+
- `git` on PATH (for the `git+` install URL)
|
|
48
|
+
|
|
49
|
+
## License
|
|
50
|
+
|
|
51
|
+
MIT — see [LICENSE](https://github.com/cooked-ham/hamgoose/blob/main/LICENSE).
|
package/bin/hamgoose.mjs
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* @cooked-ham/hamgoose — npm launcher for the hamgoose Goose extension.
|
|
4
|
+
*
|
|
5
|
+
* hamgoose itself is a Python stdio MCP server (github.com/cooked-ham/hamgoose).
|
|
6
|
+
* This package is a thin, dependency-free launcher with one job: make
|
|
7
|
+
* `npx @cooked-ham/hamgoose` just work. It never contains mission logic.
|
|
8
|
+
*
|
|
9
|
+
* npx -y @cooked-ham/hamgoose run the MCP stdio server (what Goose spawns)
|
|
10
|
+
* npx @cooked-ham/hamgoose install install the Python package (idempotent)
|
|
11
|
+
* npx @cooked-ham/hamgoose register install + register with Goose
|
|
12
|
+
* npx @cooked-ham/hamgoose --version print version
|
|
13
|
+
*/
|
|
14
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
15
|
+
import { existsSync } from "node:fs";
|
|
16
|
+
import process from "node:process";
|
|
17
|
+
import path from "node:path";
|
|
18
|
+
import { createRequire } from "node:module";
|
|
19
|
+
|
|
20
|
+
const REPO = "https://github.com/cooked-ham/hamgoose.git";
|
|
21
|
+
const WIN = process.platform === "win32";
|
|
22
|
+
const require = createRequire(import.meta.url);
|
|
23
|
+
const VERSION = require("../package.json").version;
|
|
24
|
+
|
|
25
|
+
/** Run a command, capture output. cmd may be a "tool -flag" string (Windows). */
|
|
26
|
+
function run(cmd, args = [], inherit = false) {
|
|
27
|
+
const r = spawnSync(cmd, args, {
|
|
28
|
+
encoding: "utf8",
|
|
29
|
+
shell: WIN,
|
|
30
|
+
stdio: inherit ? "inherit" : "pipe",
|
|
31
|
+
});
|
|
32
|
+
return { code: r.status ?? 1, out: ((r.stdout || "") + (r.stderr || "")).trim() };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function which(name) {
|
|
36
|
+
const r = WIN
|
|
37
|
+
? run("where", [name])
|
|
38
|
+
: run("sh", ["-c", `command -v ${name}`]);
|
|
39
|
+
if (r.code !== 0) return null;
|
|
40
|
+
const line = r.out.split(/\r?\n/).find(Boolean);
|
|
41
|
+
return line || null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function pythonCandidates() {
|
|
45
|
+
return WIN ? ["py -3", "python", "python3"] : ["python3", "python"];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Find a Python >= 3.11 command string, or null. */
|
|
49
|
+
function findPython() {
|
|
50
|
+
for (const py of pythonCandidates()) {
|
|
51
|
+
const ok = run(py, [
|
|
52
|
+
"-c",
|
|
53
|
+
"import sys; sys.exit(0 if sys.version_info >= (3,11) else 1)",
|
|
54
|
+
]);
|
|
55
|
+
if (ok.code === 0) return py;
|
|
56
|
+
}
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** pip-install the Python package (idempotent). Returns success bool. */
|
|
61
|
+
function pipInstall(py) {
|
|
62
|
+
console.log(`Installing hamgoose from ${REPO} …`);
|
|
63
|
+
let r = run(py, ["-m", "pip", "install", "--user", REPO], true);
|
|
64
|
+
if (r.code !== 0 && /externally managed/i.test(r.out)) {
|
|
65
|
+
console.log("Retrying with --break-system-packages (PEP 668)…");
|
|
66
|
+
r = run(py, ["-m", "pip", "install", "--user", "--break-system-packages", REPO], true);
|
|
67
|
+
}
|
|
68
|
+
if (r.code !== 0) {
|
|
69
|
+
console.error("\nInstall failed. Manual fallback:\n " +
|
|
70
|
+
` ${py} -m pip install ${REPO}\n` +
|
|
71
|
+
" (git must be on PATH for the git+ URL; see the repo README for a zip fallback)");
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Locate the installed `hamgoose` console script. Returns full path or null. */
|
|
78
|
+
function resolveServer(py) {
|
|
79
|
+
const onPath = which("hamgoose");
|
|
80
|
+
if (onPath) return onPath;
|
|
81
|
+
if (py) {
|
|
82
|
+
const base = run(py, ["-m", "site", "--user-base"]);
|
|
83
|
+
if (base.code === 0) {
|
|
84
|
+
const p = WIN
|
|
85
|
+
? path.join(base.out.trim(), "Scripts", "hamgoose.exe")
|
|
86
|
+
: path.join(base.out.trim(), "bin", "hamgoose");
|
|
87
|
+
if (existsSync(p)) return p;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** Ensure the Python package is installed. Returns server path or null. */
|
|
94
|
+
function ensureInstalled(quiet = false) {
|
|
95
|
+
const py = findPython();
|
|
96
|
+
let server = resolveServer(py);
|
|
97
|
+
if (server) {
|
|
98
|
+
if (!quiet) console.log(`hamgoose already installed (${server})`);
|
|
99
|
+
return server;
|
|
100
|
+
}
|
|
101
|
+
if (!py) {
|
|
102
|
+
console.error("Python 3.11+ not found. Install one (python.org or `uv`) and retry —\n" +
|
|
103
|
+
" or: uv python install 3.12 && uv tool install git+" + REPO);
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
if (!pipInstall(py)) return null;
|
|
107
|
+
server = resolveServer(py);
|
|
108
|
+
if (!quiet) console.log(server ? `Installed: ${server}` : "Installed (add your Python Scripts dir to PATH, or rerun).");
|
|
109
|
+
return server;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function printHelp() {
|
|
113
|
+
console.log(`hamgoose npm launcher v${VERSION}
|
|
114
|
+
Runs the Python hamgoose server (github.com/cooked-ham/hamgoose).
|
|
115
|
+
|
|
116
|
+
Usage:
|
|
117
|
+
npx -y @cooked-ham/hamgoose run the MCP stdio server (what Goose spawns)
|
|
118
|
+
npx @cooked-ham/hamgoose install install the Python package (idempotent)
|
|
119
|
+
npx @cooked-ham/hamgoose register install + register with Goose's config
|
|
120
|
+
npx @cooked-ham/hamgoose --version print version
|
|
121
|
+
|
|
122
|
+
Use with Goose: Add Extension (STDIO), command:
|
|
123
|
+
npx -y @cooked-ham/hamgoose`);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const [, , ...args] = process.argv;
|
|
127
|
+
|
|
128
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
129
|
+
printHelp();
|
|
130
|
+
process.exit(0);
|
|
131
|
+
}
|
|
132
|
+
if (args.includes("--version")) {
|
|
133
|
+
console.log(VERSION);
|
|
134
|
+
process.exit(0);
|
|
135
|
+
}
|
|
136
|
+
if (args[0] === "install") {
|
|
137
|
+
process.exit(ensureInstalled(false) ? 0 : 1);
|
|
138
|
+
}
|
|
139
|
+
if (args[0] === "register") {
|
|
140
|
+
const py = findPython();
|
|
141
|
+
const server = ensureInstalled(false) || resolveServer(py);
|
|
142
|
+
if (!server) process.exit(1);
|
|
143
|
+
console.log("Registering with Goose…");
|
|
144
|
+
const r = spawnSync(server, ["register"], { stdio: "inherit", shell: WIN });
|
|
145
|
+
process.exit(r.status ?? 1);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Default: stdio server mode (Goose's extension command).
|
|
149
|
+
const server = ensureInstalled(true);
|
|
150
|
+
if (!server) process.exit(1);
|
|
151
|
+
const child = spawn(server, [], { stdio: "inherit", shell: WIN });
|
|
152
|
+
child.on("exit", (code) => process.exit(code ?? 0));
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@cooked-ham/hamgoose",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "npm launcher for hamgoose — Factory-Droid-style Mission orchestration extension for Goose (goal → plan → approve → isolated workers → validated code)",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"goose",
|
|
7
|
+
"goose-extension",
|
|
8
|
+
"mcp",
|
|
9
|
+
"agent",
|
|
10
|
+
"orchestration",
|
|
11
|
+
"missions",
|
|
12
|
+
"hamgoose"
|
|
13
|
+
],
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"author": "cooked-ham",
|
|
16
|
+
"homepage": "https://github.com/cooked-ham/hamgoose",
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/cooked-ham/hamgoose.git"
|
|
20
|
+
},
|
|
21
|
+
"bugs": {
|
|
22
|
+
"url": "https://github.com/cooked-ham/hamgoose/issues"
|
|
23
|
+
},
|
|
24
|
+
"bin": {
|
|
25
|
+
"hamgoose": "bin/hamgoose.mjs"
|
|
26
|
+
},
|
|
27
|
+
"files": [
|
|
28
|
+
"bin",
|
|
29
|
+
"README.md"
|
|
30
|
+
],
|
|
31
|
+
"engines": {
|
|
32
|
+
"node": ">=18"
|
|
33
|
+
},
|
|
34
|
+
"os": [
|
|
35
|
+
"win32",
|
|
36
|
+
"darwin",
|
|
37
|
+
"linux"
|
|
38
|
+
]
|
|
39
|
+
}
|