skalpel 4.0.5 → 4.0.7
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 +8 -3
- package/package.json +1 -1
- package/postinstall.mjs +18 -0
- package/skalpel-setup.mjs +43 -0
package/README.md
CHANGED
|
@@ -4,13 +4,18 @@ Behavioral graph for AI-assisted coding. Learns how you work from your Claude Co
|
|
|
4
4
|
steers the model in real time — off the loops, re-explains, and re-fixes that eat your day.
|
|
5
5
|
|
|
6
6
|
```bash
|
|
7
|
-
|
|
8
|
-
skalpel # sign in with Google → build your graph → see your insights → you're live
|
|
7
|
+
npx skalpel@latest # sign in with Google → build your graph → see your insights → you're live
|
|
9
8
|
```
|
|
10
9
|
|
|
10
|
+
That's it — no global install. `npx` fetches the latest and runs the one-time setup (which copies
|
|
11
|
+
the hooks into `~/.skalpel` so they persist afterward). It sidesteps every machine-specific install
|
|
12
|
+
failure: no global-bin `PATH` to configure, no `sudo`, and it's always the newest version — so a
|
|
13
|
+
stale copy can never leave you on old code. Prefer a permanent install? `npm install -g skalpel`
|
|
14
|
+
still works and behaves identically.
|
|
15
|
+
|
|
11
16
|
## What it does
|
|
12
17
|
|
|
13
|
-
- **`skalpel`** (or
|
|
18
|
+
- **`npx skalpel@latest`** (or `skalpel` after a global install) runs the onboarding: red-banner wordmark → **Sign in with
|
|
14
19
|
Google** (AWS-hosted, no localhost) → **builds your graph** (async, live progress) → **reveals your
|
|
15
20
|
insights** (your hours, rework %, signature trap, biggest time-sink) → wires the hooks → you're live.
|
|
16
21
|
- After that, every prompt in Claude Code / Codex pulls your own behavioral graph via a fast hook, and a
|
package/package.json
CHANGED
package/postinstall.mjs
CHANGED
|
@@ -57,8 +57,26 @@ function stageStatusline() {
|
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
// If this postinstall ran under `sudo npm install -g`, we're root but a REAL user invoked it — so
|
|
61
|
+
// anything we just created in their HOME (the staged ~/.skalpel) is root-owned, a landmine for the
|
|
62
|
+
// later non-root `skalpel` (that's Ryaan's EACCES on copyfile into ~/.skalpel/hooks). Chown it back to
|
|
63
|
+
// the invoking user so the sudo install path isn't left broken.
|
|
64
|
+
function fixSudoOwnership() {
|
|
65
|
+
try {
|
|
66
|
+
if (process.platform === "win32") return; // no uid / sudo model
|
|
67
|
+
if (typeof process.getuid !== "function" || process.getuid() !== 0) return; // not root — nothing to undo
|
|
68
|
+
const uid = process.env.SUDO_UID;
|
|
69
|
+
if (!uid) return; // genuinely root (not via sudo) — don't touch their files
|
|
70
|
+
const gid = process.env.SUDO_GID || uid;
|
|
71
|
+
spawnSync("chown", ["-R", `${uid}:${gid}`, join(homedir(), ".skalpel")], q);
|
|
72
|
+
} catch {
|
|
73
|
+
/* best-effort — worst case the user runs `sudo chown -R $(whoami) ~/.skalpel` themselves */
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
60
77
|
retireLegacyDaemon();
|
|
61
78
|
stageStatusline();
|
|
79
|
+
fixSudoOwnership();
|
|
62
80
|
|
|
63
81
|
// A quiet nudge — the real onboarding (Google sign-in → build graph → insights) runs on `skalpel`.
|
|
64
82
|
if (process.stdout.isTTY && !process.env.CI) {
|
package/skalpel-setup.mjs
CHANGED
|
@@ -727,6 +727,48 @@ async function revealInsights(report) {
|
|
|
727
727
|
);
|
|
728
728
|
}
|
|
729
729
|
|
|
730
|
+
// Self-heal preflight. The install failures friends hit were never our code — they were the MACHINE:
|
|
731
|
+
// a ~/.skalpel left root-owned by a past `sudo npm i -g` (so we can't stage hooks into it), or a Node
|
|
732
|
+
// too old for the hook runtime. Catch both HERE with a one-line fix instead of a cryptic EACCES stack
|
|
733
|
+
// trace five calls deep. Runs only on the setup path (login/logout/uninstall return before this).
|
|
734
|
+
function preflight() {
|
|
735
|
+
const major = Number.parseInt(process.versions.node.split(".")[0], 10);
|
|
736
|
+
if (major < 18) {
|
|
737
|
+
console.error(
|
|
738
|
+
` ${R}✗${X} skalpel needs Node 18+ — you have ${process.versions.node}. Update Node, then re-run.`,
|
|
739
|
+
);
|
|
740
|
+
process.exit(1);
|
|
741
|
+
}
|
|
742
|
+
const dir = join(homedir(), ".skalpel");
|
|
743
|
+
try {
|
|
744
|
+
mkdirSync(dir, { recursive: true });
|
|
745
|
+
accessSync(dir, constants.W_OK);
|
|
746
|
+
return; // writable — good to go
|
|
747
|
+
} catch {
|
|
748
|
+
/* not writable — try to self-heal, then re-check below */
|
|
749
|
+
}
|
|
750
|
+
// If we're root via `sudo` (SUDO_UID set), hand ownership back to the real user so the later
|
|
751
|
+
// non-root hook writes don't EACCES. On Windows there's no uid model — getuid is undefined, skip.
|
|
752
|
+
if (typeof process.getuid === "function" && process.getuid() === 0 && process.env.SUDO_UID) {
|
|
753
|
+
spawnSync(
|
|
754
|
+
"chown",
|
|
755
|
+
["-R", `${process.env.SUDO_UID}:${process.env.SUDO_GID || process.env.SUDO_UID}`, dir],
|
|
756
|
+
{ stdio: "ignore" },
|
|
757
|
+
);
|
|
758
|
+
}
|
|
759
|
+
try {
|
|
760
|
+
accessSync(dir, constants.W_OK);
|
|
761
|
+
} catch {
|
|
762
|
+
const who = process.env.USER || process.env.LOGNAME || "$(whoami)";
|
|
763
|
+
console.error(
|
|
764
|
+
` ${R}✗${X} ~/.skalpel isn't writable — a past \`sudo\` install left it root-owned.\n` +
|
|
765
|
+
` One line fixes it, then re-run:\n\n` +
|
|
766
|
+
` ${B}sudo chown -R ${who} ~/.skalpel${X}\n`,
|
|
767
|
+
);
|
|
768
|
+
process.exit(1);
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
|
|
730
772
|
async function main() {
|
|
731
773
|
if (sub === "uninstall") {
|
|
732
774
|
spawnSync("node", [join(__dir, "install.mjs"), "--uninstall"], { stdio: "inherit" });
|
|
@@ -766,6 +808,7 @@ async function main() {
|
|
|
766
808
|
mkdirSync(cfgDir, { recursive: true });
|
|
767
809
|
writeFileSync(cfgPath, JSON.stringify(cfg, null, 2));
|
|
768
810
|
}
|
|
811
|
+
preflight(); // fail fast + fixable if the machine can't take the install (root-owned dir / old Node)
|
|
769
812
|
banner();
|
|
770
813
|
|
|
771
814
|
// 1) SIGN IN with Google (once). SKALPEL_USER (dev) or an existing ~/.skalpel/auth.json skips it.
|