@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.
Files changed (3) hide show
  1. package/README.md +51 -0
  2. package/bin/hamgoose.mjs +152 -0
  3. 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).
@@ -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
+ }