@techgoblin/gobstack 0.4.4-beta.1 → 0.4.4-beta.2

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 CHANGED
@@ -5,13 +5,34 @@ installs an **executable rule manifest**, a set of **project-local skills**, and
5
5
  **installer/verifier pair** into any project — so that an AI coding session never re-improvises,
6
6
  and a rule that cannot be checked is counted rather than asserted.
7
7
 
8
- Install it from npm — the one documented install path:
8
+ Install it from npm — globally; it is a CLI, not a library:
9
9
 
10
- npm i -g @techgoblin/gobstack # gives you the `goblin` CLI
10
+ npm install -g @techgoblin/gobstack@beta # gives you the `goblin` CLI (and `gob`)
11
+ npx @techgoblin/gobstack@beta init # or the one-shot: run the wizard, install nothing globally
11
12
 
12
13
  ## Install
13
14
 
14
- npm i -g @techgoblin/gobstack
15
+ **Global, not local.** gobstack is a CLI with zero runtime dependencies. Install it once per
16
+ machine:
17
+
18
+ npm install -g @techgoblin/gobstack@beta
19
+
20
+ or run a single command without installing:
21
+
22
+ npx @techgoblin/gobstack@beta init
23
+
24
+ **Do NOT add it to an app project's `package.json`.** A `npm install @techgoblin/gobstack` (or a
25
+ `package.json` dependency) inside your app pollutes the app's lockfile with a package the app
26
+ never imports, and can fail resolution outright with `ERESOLVE` when the app's own peer
27
+ dependencies disagree with npm's. If you see `ERESOLVE` after a local install, remove the
28
+ dependency from `package.json` and install globally instead.
29
+
30
+ **The two-layer model.** The global install gives you the CLI only. `goblin init` (or
31
+ `goblin install --target <dir> --class A`) then vendors a self-contained engine into the target
32
+ repo under `.goblin/` — verifier, manifest, ban probes, skills, all of it. That second layer is
33
+ why an initialized repo keeps working on machines with **no gobstack installed at all**: the
34
+ engine lives in the repo, not in your `node_modules`, and `bash .goblin/bin/goblin-verify` (or a
35
+ plain `git` + `bash` box) is the only runtime the repo's gate needs.
15
36
 
16
37
  Then, from any project:
17
38
 
@@ -63,7 +84,7 @@ The measurement and the vacuous-pass reading are in `docs/CONTRACTS.md`.
63
84
  | `goblin bans` | run the ban list (per-pattern red lines over the source tree) |
64
85
  | `goblin audit` | check recorded dependency claims against live advisory feeds — the only command that touches the network |
65
86
  | `goblin install` | install the manifest, skills and verifier into a target repo |
66
- | `goblin uninstall` | remove everything an install wrote (npm shim `goblin install --target <dir> --uninstall`) |
87
+ | `goblin uninstall` | remove everything an install wrote, byte-exactly (`goblin install --target <dir> --uninstall` is the same job) |
67
88
  | `goblin upgrade` | migrate a repo to the shared global engine at `~/.goblin/engine` — 8 steps, two commits, one report |
68
89
  | `goblin doctor` | one run across the platforms below: DETECTED / NOT-DETECTED / DRIFT per platform |
69
90
  | `goblin emit` | write the skills + context block for one platform (`--scope project` or `global`); `--unshadow` removes a hermes project skill whose hash equals the source |
@@ -138,11 +159,44 @@ failure**, so that file is the one that matters most.
138
159
 
139
160
  ## Uninstall
140
161
 
141
- goblin install --target <dir> --uninstall
162
+ gobstack lives in three layers. Each is removed by its own command, and removing one never
163
+ touches the others.
164
+
165
+ **(a) The global CLI** — the npm package itself:
166
+
167
+ npm uninstall -g @techgoblin/gobstack
168
+
169
+ This removes the `goblin` and `gob` commands from the machine and nothing else: no project, no
170
+ repo, no `.goblin/` directory anywhere is touched. Repos you already initialized keep working
171
+ fully — the engine is vendored into each repo's `.goblin/`, so the CLI's absence removes no
172
+ capability (you lose the installer/upgrade/emit entry points, not the gate; see layer (c) for
173
+ the machine-level skills the CLI wrote).
174
+
175
+ **(b) A project's harness** — the `.goblin/` tree an install created in one repo:
176
+
177
+ goblin uninstall --target .
178
+
179
+ (equivalently `goblin install --target . --uninstall`; through the short alias:
180
+ `gob uninstall --target .`). The uninstall is **byte-exact**: it removes exactly the files
181
+ `installed.json` records — hash-compared preimages, so a file you edited after install is
182
+ reported and kept, never clobbered — then every directory that leaves empty. After it, the repo
183
+ has zero goblin files; only the project's own record (`HANDOFF.md`, `AGENTS.md`, `reviews/`, the
184
+ `.gitignore` block) survives, because that is the project's, not the harness's to delete. And
185
+ because the engine is vendored, the repo needs no gobstack installed to run this — it is
186
+ self-contained until the moment you remove it.
187
+
188
+ **(c) Global agent skills** — the machine-level skills an `emit --scope global` wrote outside any
189
+ repo:
190
+
191
+ goblin emit --undo --platform <p> --scope global
192
+
193
+ (`--undo` is the same byte-exact reversal as `--uninstall`, under its friendlier name). By hand,
194
+ the same job is deleting the platform's anchor entries: `~/.claude/skills/goblin-*` (and the
195
+ equivalents under `~/.hermes`, `~/.copilot`, `~/.cursor`, `~/.config/opencode`, `~/.codex`,
196
+ `~/.gemini` — `goblin doctor` lists which platforms were detected).
142
197
 
143
- Removes the installed artifacts, `.goblin/goblin.yaml` and every directory that leaves empty, and
144
- leaves `HANDOFF.md`, `AGENTS.md`, `ROUND-000-SPEC.md`, `reviews/` and the `.gitignore` block —
145
- the project's record is not the harness's to delete.
198
+ The short version, for a full removal from a machine and its repos: (c) first, then (b) in each
199
+ initialized repo, then (a).
146
200
 
147
201
  ## Re-pin the referenced standard
148
202
 
package/bin/goblin-emit CHANGED
@@ -80,6 +80,7 @@ goblin emit — per-platform emission (W4a, the seven platforms since W4b).
80
80
  --skills core (the 6 procedure skills), all (every shipped skill), none (context block only)
81
81
  --source the skills payload; default: this CLI checkout's skills/
82
82
  --uninstall reverse every recorded write for the platform, byte-exactly, newest first
83
+ (--undo is the same job under its friendlier name)
83
84
  --unshadow hermes only: remove project skills whose hash EQUALS the source;
84
85
  refuse-and-name on any that differ (a real local edit is never auto-removed)
85
86
  --dry-run print the full write plan (every path + the priced index size), write nothing
@@ -127,6 +128,7 @@ while [ $# -gt 0 ]; do
127
128
  --target) TARGET="${2:-}"; OPT_TARGET=1; shift 2 ;;
128
129
  --source) SRCSKILLS="${2:-}"; shift 2 ;;
129
130
  --uninstall) UNINSTALL=1; shift ;;
131
+ --undo) UNINSTALL=1; shift ;;
130
132
  --unshadow) UNSHADOW=1; shift ;;
131
133
  --dry-run) DRYRUN=1; shift ;;
132
134
  --strict) STRICT=1; shift ;;
package/bin/goblin.js CHANGED
@@ -13,7 +13,13 @@
13
13
  // goblin doctor [...] -> bin/goblin-doctor (W4a)
14
14
  // goblin emit [...] -> bin/goblin-emit (W4a)
15
15
  // goblin init [...] -> bin/goblin-init (W6, the first-run wizard)
16
- // anything else (install) -> bin/goblin-install
16
+ // goblin uninstall [--target <dir>] -> bin/goblin-install --uninstall
17
+ // goblin install [...] -> bin/goblin-install (the one legacy fallback)
18
+ // no args | -h/--help | any other unrecognized first arg
19
+ // -> this file's short usage, exit 2. A bare `goblin`
20
+ // used to fall through into the installer; a typo
21
+ // (`goblin inti`) silently installed too. Both now
22
+ // print the usage and stop.
17
23
  //
18
24
  // Non-negotiables (§4.3): args are passed as an ARRAY, never a shell string (no
19
25
  // injection surface); `bash` is named explicitly (a packager stripping the
@@ -40,9 +46,58 @@ if (arg0 === "--version" || arg0 === "-V" || arg0 === "-v") {
40
46
 
41
47
  const SCRIPT = { verify: "goblin-verify", bans: "goblin-bans", audit: "goblin-audit", upgrade: "goblin-upgrade", doctor: "goblin-doctor", emit: "goblin-emit", init: "goblin-init" };
42
48
  const [cmd, ...rest] = process.argv.slice(2);
43
- const target = SCRIPT[cmd] ?? "goblin-install"; // install → goblin-install (v1); init is a real subcommand since W6
49
+
50
+ // No args, a help flag, or an unrecognized first arg: short usage, exit 2. The one survivor of
51
+ // the old catch-all fallback is the literal `install` first arg — bare `goblin` mapped to the
52
+ // installer through npm's bin default, and a typo (`goblin inti`) silently installed into
53
+ // whatever directory the shell sat in. A bare subcommand-less `goblin install ...` keeps the
54
+ // installer; everything else stops here and names the word it did not know.
55
+ function usage() {
56
+ process.stderr.write(
57
+ [
58
+ "goblin <command>",
59
+ "",
60
+ " goblin init start here — the guided first step (detect, class, emit, first verify)",
61
+ " goblin verify run the rule matrix against the current repo",
62
+ " goblin bans run the ban list (per-pattern red lines over the source tree)",
63
+ " goblin audit check recorded dependency claims against live advisory feeds",
64
+ " goblin upgrade migrate a repo to the shared global engine at ~/.goblin/engine",
65
+ " goblin doctor one detection/drift run across the agent platforms",
66
+ " goblin emit write the skills + context block for one platform",
67
+ " goblin uninstall --target . remove exactly what an install wrote (preimages)",
68
+ "",
69
+ "start here: goblin init",
70
+ "uninstall: npm uninstall -g @techgoblin/gobstack",
71
+ "",
72
+ ].join("\n"));
73
+ }
74
+
75
+ if (cmd === undefined || cmd.startsWith("-")) {
76
+ usage();
77
+ process.exit(2);
78
+ }
79
+ if (!SCRIPT[cmd] && cmd !== "install" && cmd !== "uninstall") {
80
+ process.stderr.write(`goblin: unrecognized command: ${cmd}\n\n`);
81
+ usage();
82
+ process.exit(2);
83
+ }
84
+
85
+ let target;
86
+ let extra = [];
87
+ if (cmd === "install") {
88
+ target = "goblin-install"; // the one legacy fallback, kept verbatim
89
+ } else if (cmd === "uninstall") {
90
+ // `goblin uninstall --target <dir>` routes into the installer's uninstall job — the shape
91
+ // docs/GUIDE.md and README already promise. `--uninstall` is appended FIRST so the user's
92
+ // own `--target <dir>` and options still parse, and a stray literal `--uninstall` cannot
93
+ // appear twice.
94
+ target = "goblin-install";
95
+ extra = ["--uninstall"];
96
+ } else {
97
+ target = SCRIPT[cmd];
98
+ }
44
99
  const file = path.join(__dirname, "..", "bin", target);
45
100
  // execPath-independent: call bash explicitly so Windows-WSL/Git-Bash works and no
46
101
  // shebang resolution is needed.
47
- const r = spawnSync("bash", [file, ...rest], { stdio: "inherit" });
102
+ const r = spawnSync("bash", [file, ...extra, ...rest], { stdio: "inherit" });
48
103
  process.exit(r.status ?? 2);
package/package.json CHANGED
@@ -1,9 +1,10 @@
1
1
  {
2
2
  "name": "@techgoblin/gobstack",
3
- "version": "0.4.4-beta.1",
3
+ "version": "0.4.4-beta.2",
4
4
  "description": "Agent-discipline toolkit: one verify command, an enforcement matrix, and LIMITS. bash engine, npm shim.",
5
5
  "bin": {
6
- "goblin": "bin/goblin.js"
6
+ "goblin": "bin/goblin.js",
7
+ "gob": "bin/goblin.js"
7
8
  },
8
9
  "publishConfig": {
9
10
  "access": "public"