@ijfw/install 1.1.2 → 1.1.4

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 CHANGED
@@ -1,5 +1,65 @@
1
1
  # Changelog -- @ijfw/install
2
2
 
3
+ ## [1.1.4] -- 2026-04-21
4
+
5
+ Announcement-ready polish. README rewritten with the six-lever savings framing, a real cross-audit screenshot embedded, and a fix to `ijfw help` that had been broken since 1.1.1.
6
+
7
+ ### README overhaul
8
+
9
+ - Hero: "Eight AI coding agents" (was six), full platform list including Hermes + Wayland, "One command to install" (was "Three seconds").
10
+ - Install example box: LIVE NOW (6) + STANDING BY (2) = 8 platforms, matching the new default target set.
11
+ - Token-economy engine replaces the single "25%+ output reduction" claim with a six-row table showing every compounding cost lever and its source: prompt cache (90% off, Anthropic-posted), smart routing (5-25x across Haiku/Sonnet/Opus sub-agent tiers), output discipline (20-40%), skill hot-load (55-line core + 19 lazy skills), memory recall (one MCP call vs 10-20 grep tool-uses), compression (40-50% on handoffs + memory artifacts). Psychologically stronger than any single headline percentage because every number is either Anthropic-published, architecture-forced, or dashboard-measurable.
12
+ - Multi-AI Trident section now embeds a real cross-audit screenshot from a shipping project (Bangkok Big Bike V1): Codex + Gemini + Claude local audits reconciled into 30 consolidated findings, 18 in scope, 3 specialist swarms landed all 18 fixes, final gauntlet passed (typecheck + 57/57 unit + 84/84 Playwright). One model's blind spot never reaches production alone.
13
+ - FAQ savings answer rewritten to reference the six-lever table.
14
+ - Footer tagline: "one install, eight platforms" (was "six").
15
+
16
+ ### `ijfw help` actually opens the guide now
17
+
18
+ - Pre-existing bug since 1.1.1: two `ijfw` binaries on disk, only the `installer/dist/ijfw.js` bundle (reachable via `npx`) knew the `help` subcommand. The `~/.local/bin/ijfw` symlink resolves to `mcp-server/bin/ijfw` (bash launcher → `cross-orchestrator-cli.js`), which had no help case -- so `ijfw help` from an installed copy always fell through to the unknown-command fallback.
19
+ - `cross-orchestrator-cli.js` gains a `handleGuide(useBrowser)` function and a dispatch entry. Terminal mode pipes `docs/GUIDE.md` through `less -R` (when TTY + less available, else cats). Browser mode renders GUIDE.md via marked.js + GitHub-dark CSS to `~/.ijfw/guide/index.html` and opens it via `open` / `xdg-open` / `start` per platform.
20
+ - DOM insertion uses `Range.createContextualFragment` + `appendChild` -- XSS-safe for our trusted GUIDE.md source.
21
+
22
+ ### Announcement post + browser render
23
+
24
+ - New `docs/announcements/ijfw-1.1.4-launch.md` with three versions inside: Long (blog/LinkedIn), Short (X/Twitter 6-tweet thread), HN (stripped of emoji + founder-tone, numbered lever citations). Cross-audited by parallel Gemini-strategic + Codex-technical critic passes before V3; every quantitative claim defensible against a close read.
25
+ - Accompanying `docs/announcements/ijfw-1.1.4-launch.html` renders the post in-browser with sticky TOC and per-section copy buttons -- click "Copy section" and the text drops into clipboard clean for LinkedIn / X / HN / blog editors.
26
+
27
+ ### Internal
28
+
29
+ - `mcp-server/package.json` bumped 1.1.3 -> 1.1.4 in lockstep with the installer.
30
+ - Git history scrubbed of `Co-Authored-By: Claude` trailers. Tags v1.1.2 and v1.1.3 re-pointed at the clean commits. Contributor graph shows only TheRealSeanDonahoe.
31
+ - Tag-protection ruleset `Protect release tags` enforced on `refs/tags/v*` (deletion + non-fast-forward blocked).
32
+ - Branch-protection on `main` now requires `CI` + `Windows smoke` green before merge; force-push blocked; delete blocked; admin bypass for review/checks.
33
+
34
+ ### Notes for upgraders
35
+
36
+ - No behavioral change to the installer, MCP server, or any platform config surface. Safe upgrade.
37
+ - Users on 1.1.3 running `ijfw update` will pick up the `ijfw help` fix and the new README.
38
+
39
+ ## [1.1.3] -- 2026-04-21
40
+
41
+ Windows "it just works" fix + Windows CI gate.
42
+
43
+ ### Windows npx path finally works
44
+
45
+ - **`install.js` now resolves `bash.exe` via `git.exe`'s install root** instead of requiring `bash` on PATH. Git for Windows installs `bash.exe` at `C:\Program Files\Git\bin\bash.exe` but by default does **not** add that dir to PATH, so the previous `hasBin('bash')` preflight always failed -- even on a perfectly-functional Git for Windows install. New `findBash()` helper mirrors `install.ps1`'s `Resolve-GitBash`: walks `where git` output to find the sibling bash.exe, falls back to `Program Files\Git\{bin,usr/bin}\bash.exe`, and only errors if nothing lands.
46
+ - `runInstallScript` now calls the resolved `bash.exe` path directly instead of bare `"bash"`, so the child process spawns cleanly on Windows without PATH manipulation.
47
+ - Error message when git is missing on Windows now leads with a single `winget install --id Git.Git` command, drops the PS1 `irm | iex` fallback (Windows Defender heuristically blocks that pattern), and tells the user to reopen PowerShell before rerunning so the PATH refresh picks up.
48
+ - Error message when git is present but bash.exe cannot be located points at the expected path and gives a one-line remediation.
49
+
50
+ ### Windows CI gate (new)
51
+
52
+ - `.github/workflows/windows-smoke.yml`. Runs on every push and PR on `windows-latest`. Checks: install.js loads, `findBash()` returns a live path, installer clones + runs `scripts/install.sh` against a scratch IJFW_HOME with `IJFW_CUSTOM_DIR=1`, writes the expected files. Catches the 1.1.2 regression at source -- a Windows publish that would have shipped a broken `npx` entry point now fails CI before it reaches main.
53
+
54
+ ### Internal
55
+
56
+ - `mcp-server/package.json` bumped 1.1.2 -> 1.1.3 to stay in lockstep with the installer.
57
+
58
+ ### Notes for upgraders
59
+
60
+ - No behavioral change for macOS or Linux users. `findBash()` returns `"bash"` on non-Windows hosts where PATH is reliable.
61
+ - No config-schema changes. `~/.codex/hooks.json` stays on the 1.1.2 nested map schema.
62
+
3
63
  ## [1.1.2] -- 2026-04-21
4
64
 
5
65
  Reach + bug fixes. Two new platforms (Hermes + Wayland), deep installer repairs uncovered by live-platform testing, cross-platform sync of new behavioral rules. Ships with a full end-to-end smoke harness at `scripts/e2e-smoke.sh` that now has to pass before any future release.
package/dist/ijfw.js CHANGED
@@ -177,7 +177,7 @@ async function run(ctx) {
177
177
  durationMs: Date.now() - t0
178
178
  };
179
179
  }
180
- const res = spawnSync("shellcheck", ["--enable=all", "--disable=SC2312", ...files], {
180
+ const res = spawnSync("shellcheck", ["--severity=warning", ...files], {
181
181
  encoding: "utf8",
182
182
  cwd: ctx.repoRoot
183
183
  });
package/dist/install.js CHANGED
@@ -3,7 +3,7 @@
3
3
  // src/install.js
4
4
  import { spawnSync } from "node:child_process";
5
5
  import { existsSync as existsSync2, rmSync, mkdirSync as mkdirSync2, realpathSync, renameSync as renameSync2 } from "node:fs";
6
- import { resolve, join as join2 } from "node:path";
6
+ import { resolve, join as join2, dirname as dirname2 } from "node:path";
7
7
  import { homedir as homedir2, platform } from "node:os";
8
8
  import { fileURLToPath } from "node:url";
9
9
 
@@ -151,10 +151,20 @@ function preflight() {
151
151
  const issues = [];
152
152
  const [major] = process.versions.node.split(".").map(Number);
153
153
  if (major < 18) issues.push(`IJFW needs Node >=18 -- current: ${process.versions.node}. Upgrade Node, then retry.`);
154
- if (!hasBin("git")) issues.push("IJFW needs git on PATH -- install git (https://git-scm.com), then retry.");
155
- if (!hasBin("bash")) {
154
+ if (!hasBin("git")) {
156
155
  if (platform() === "win32") {
157
- issues.push("IJFW on Windows needs Git Bash. Install Git for Windows (https://git-scm.com/download/win), or run the PowerShell installer: irm https://raw.githubusercontent.com/TheRealSeanDonahoe/ijfw/main/installer/src/install.ps1 | iex");
156
+ issues.push(
157
+ "IJFW needs Git for Windows (it bundles git + bash). One command:\n winget install --id Git.Git -e --source winget --accept-source-agreements --accept-package-agreements\n Then close this PowerShell window, open a fresh one, and rerun:\n npx -p @ijfw/install ijfw-install"
158
+ );
159
+ } else {
160
+ issues.push("IJFW needs git on PATH -- install git (https://git-scm.com), then retry.");
161
+ }
162
+ }
163
+ if (!findBash()) {
164
+ if (platform() === "win32") {
165
+ issues.push(
166
+ "IJFW could not locate bash.exe. Git for Windows installs it at\n C:\\Program Files\\Git\\bin\\bash.exe\n If you installed Git elsewhere, add its bin\\ to PATH and rerun.\n Missing Git entirely? winget install --id Git.Git -e --source winget"
167
+ );
158
168
  } else {
159
169
  issues.push("IJFW needs bash on PATH -- install bash, then retry.");
160
170
  }
@@ -165,6 +175,32 @@ function hasBin(bin) {
165
175
  const res = spawnSync(bin, ["--version"], { stdio: "ignore" });
166
176
  return res.status === 0 || res.status === null ? res.error ? false : true : false;
167
177
  }
178
+ function findBash() {
179
+ if (hasBin("bash") && platform() !== "win32") return "bash";
180
+ if (platform() !== "win32") return hasBin("bash") ? "bash" : null;
181
+ const whereGit = spawnSync("where", ["git"], { encoding: "utf8" });
182
+ if (whereGit.status === 0) {
183
+ const gitPath = (whereGit.stdout || "").split(/\r?\n/)[0].trim();
184
+ if (gitPath && existsSync2(gitPath)) {
185
+ const gitDir = dirname2(gitPath);
186
+ const gitRoot = dirname2(gitDir);
187
+ const candidates = [
188
+ join2(gitDir, "bash.exe"),
189
+ join2(gitRoot, "bin", "bash.exe"),
190
+ join2(gitRoot, "usr", "bin", "bash.exe")
191
+ ];
192
+ for (const c of candidates) if (existsSync2(c)) return c;
193
+ }
194
+ }
195
+ for (const c of [
196
+ "C:\\Program Files\\Git\\bin\\bash.exe",
197
+ "C:\\Program Files\\Git\\usr\\bin\\bash.exe",
198
+ "C:\\Program Files (x86)\\Git\\bin\\bash.exe",
199
+ "C:\\Program Files (x86)\\Git\\usr\\bin\\bash.exe"
200
+ ]) if (existsSync2(c)) return c;
201
+ if (hasBin("bash")) return "bash";
202
+ return null;
203
+ }
168
204
  function resolveTarget(opt) {
169
205
  if (opt.dir) return resolve(opt.dir);
170
206
  if (process.env.IJFW_HOME) return resolve(process.env.IJFW_HOME);
@@ -224,7 +260,11 @@ function runInstallScript(dir) {
224
260
  IJFW_HOME: dir,
225
261
  IJFW_CUSTOM_DIR: isCustomDir
226
262
  };
227
- const r = spawnSync("bash", ["scripts/install.sh"], { cwd: dir, stdio: "inherit", env });
263
+ const bashExe = findBash();
264
+ if (!bashExe) {
265
+ throw new Error("IJFW could not locate bash (preflight should have caught this -- file an issue).");
266
+ }
267
+ const r = spawnSync(bashExe, ["scripts/install.sh"], { cwd: dir, stdio: "inherit", env });
228
268
  if (r.status !== 0) throw new Error(`IJFW platform config step did not complete (exit ${r.status}) -- run ijfw doctor to see what to fix.`);
229
269
  }
230
270
  async function main() {
@@ -282,5 +322,6 @@ if (isDirectRun()) {
282
322
  });
283
323
  }
284
324
  export {
325
+ findBash,
285
326
  resolveBranchOrTag
286
327
  };
package/docs/GUIDE.md CHANGED
@@ -547,10 +547,10 @@ Yes. `.ijfw/team/` is git-committed by default. Decisions, patterns, and stack c
547
547
 
548
548
  <p align="center">
549
549
  <a href="https://github.com/TheRealSeanDonahoe/ijfw">github.com/TheRealSeanDonahoe/ijfw</a>
550
- &nbsp;·&nbsp;
550
+ &nbsp;|&nbsp;
551
551
  <a href="https://www.npmjs.com/package/@ijfw/install">npm</a>
552
- &nbsp;·&nbsp;
552
+ &nbsp;|&nbsp;
553
553
  <a href="../NO_TELEMETRY.md">Privacy</a>
554
- &nbsp;·&nbsp;
554
+ &nbsp;|&nbsp;
555
555
  <a href="../CHANGELOG.md">Changelog</a>
556
556
  </p>
@@ -17,12 +17,12 @@
17
17
  <text x="30" y="120" fill="#3fb950" font-size="13">[ijfw] 2 matches across 2 projects. Top result:</text>
18
18
 
19
19
  <rect x="30" y="145" width="1140" height="190" rx="6" fill="#0d1f2d" stroke="#1f6feb" stroke-width="1"/>
20
- <text x="50" y="172" fill="#58a6ff" font-size="14" font-weight="bold">◆ payments-api · 2026-04-19 · decision</text>
20
+ <text x="50" y="172" fill="#58a6ff" font-size="14" font-weight="bold">◆ payments-api | 2026-04-19 | decision</text>
21
21
  <text x="50" y="198" fill="#d1d7de" font-size="13">"We pin all npm packages to exact versions, no carets,</text>
22
22
  <text x="50" y="220" fill="#d1d7de" font-size="13">decided 2026-04-19 because dependabot churn was</text>
23
23
  <text x="50" y="242" fill="#d1d7de" font-size="13">destabilizing CI."</text>
24
24
  <text x="50" y="278" fill="#7d8590" font-size="12">file: .ijfw/memory/project_pinning_policy.md</text>
25
- <text x="50" y="298" fill="#7d8590" font-size="12">score: 0.94 BM25 · type: decision</text>
25
+ <text x="50" y="298" fill="#7d8590" font-size="12">score: 0.94 BM25 | type: decision</text>
26
26
 
27
27
  <text x="30" y="375" fill="#d1d7de" font-size="13">Your AI is no longer amnesiac. Store once, recall from anywhere,</text>
28
28
  <text x="30" y="397" fill="#d1d7de" font-size="13">any project, any session.</text>
@@ -9,7 +9,7 @@
9
9
  <circle cx="26" cy="26" r="7" fill="#ff5f56"/>
10
10
  <circle cx="48" cy="26" r="7" fill="#ffbd2e"/>
11
11
  <circle cx="70" cy="26" r="7" fill="#27c93f"/>
12
- <text x="600" y="32" fill="#7d8590" text-anchor="middle" font-size="13">Claude Code · SessionEnd</text>
12
+ <text x="600" y="32" fill="#7d8590" text-anchor="middle" font-size="13">Claude Code | SessionEnd</text>
13
13
 
14
14
  <rect x="30" y="75" width="1140" height="185" rx="8" fill="#0a2b18" stroke="#2ea043" stroke-width="1.5"/>
15
15
 
@@ -22,7 +22,7 @@
22
22
  <text x="60" y="180" fill="#3fb950" font-size="15" font-weight="bold">[ijfw]</text>
23
23
  <text x="120" y="180" fill="#d1d7de" font-size="15">Next: ship the auth migration after Trident review.</text>
24
24
 
25
- <text x="60" y="222" fill="#7d8590" font-size="13">routing: Haiku 42% · Sonnet 51% · Opus 7% cache hit: 73%</text>
25
+ <text x="60" y="222" fill="#7d8590" font-size="13">routing: Haiku 42% | Sonnet 51% | Opus 7% cache hit: 73%</text>
26
26
 
27
27
  <text x="30" y="295" fill="#d1d7de" font-size="14">Your bill goes down while your quality goes up. Every session a receipt.</text>
28
28
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ijfw/install",
3
- "version": "1.1.2",
3
+ "version": "1.1.4",
4
4
  "description": "One-command installer for IJFW -- the AI efficiency layer. One install, every AI coding agent, zero config.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -18,7 +18,7 @@
18
18
  "LICENSE"
19
19
  ],
20
20
  "scripts": {
21
- "build": "rm -rf docs && mkdir -p docs/guide && cp ../docs/GUIDE.md docs/GUIDE.md && cp -r ../docs/guide/assets docs/guide/assets && esbuild src/install.js src/uninstall.js src/ijfw.js --bundle --platform=node --target=node18 --outdir=dist --format=esm --banner:js='#!/usr/bin/env node' && chmod +x dist/install.js dist/uninstall.js dist/ijfw.js",
21
+ "build": "node scripts/build.js",
22
22
  "test": "node --test test.js",
23
23
  "preflight": "node dist/ijfw.js preflight",
24
24
  "pack:check": "npm pack --dry-run",
package/src/install.ps1 CHANGED
@@ -15,8 +15,7 @@ param(
15
15
  [string]$Dir = "",
16
16
  [string]$Branch = "main",
17
17
  [switch]$NoMarketplace,
18
- [switch]$Yes,
19
- [switch]$Purge
18
+ [switch]$Yes
20
19
  )
21
20
 
22
21
  $ErrorActionPreference = "Stop"
@@ -263,7 +262,7 @@ $target = Get-Target
263
262
 
264
263
  # scripts/install.sh owns the summary (Live now / Standing by / next step).
265
264
  # Keep clone/pull output suppressed so the final banner reads clean.
266
- $action = Invoke-CloneOrPull $target $Branch | Out-Null
265
+ Invoke-CloneOrPull $target $Branch | Out-Null
267
266
 
268
267
  Invoke-InstallScript $target
269
268