glimmervoid 0.0.0-stage → 0.30.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/LICENSE +48 -0
- package/README.md +195 -2
- package/dist/bin/glimmervoid.js +275 -0
- package/dist/chunks/adapters-Dho1VSY5.js +2 -0
- package/dist/chunks/adapters-zPDx1Xox.js +575 -0
- package/dist/chunks/agent-api-cli-D0MmtEdz.js +71 -0
- package/dist/chunks/agent-api-core-Dz0r21y9.js +103 -0
- package/dist/chunks/agent-setup-cli-B5Atxr3F.js +139 -0
- package/dist/chunks/ascii-figure-core-pDAwtlKO.js +45 -0
- package/dist/chunks/child-process-safe-DRGsp0TE.js +69 -0
- package/dist/chunks/child-process-safe-DvQjXsgz.js +2 -0
- package/dist/chunks/config-path-core-DFt-TemS.js +38 -0
- package/dist/chunks/config-store-CC8T3okC.js +2 -0
- package/dist/chunks/config-store-mm7MgzoL.js +1412 -0
- package/dist/chunks/contracts-DGkKNh9W.js +3020 -0
- package/dist/chunks/grok-DuU8xVLZ.js +436 -0
- package/dist/chunks/hook-command-core-DclEoE9e.js +14 -0
- package/dist/chunks/hook-relay-core-BHfYFnYV.js +88 -0
- package/dist/chunks/hook-tools-pSJia7ks.js +74 -0
- package/dist/chunks/ingest-core-DWI1Mbu4.js +342 -0
- package/dist/chunks/ingest-editor-core-BuI1gMF_.js +445 -0
- package/dist/chunks/loopback-post-C7C22eYi.js +79 -0
- package/dist/chunks/main-CuIZQPhZ.js +43559 -0
- package/dist/chunks/node-pty-preflight-CK1knGIR.js +162 -0
- package/dist/chunks/pair-cli-w-gCAF-1.js +110 -0
- package/dist/chunks/path-doctor-C23VAqeu.js +63 -0
- package/dist/chunks/refocus-core-BKQKllq7.js +38 -0
- package/dist/chunks/relay-stdin-CpHvLu6M.js +20 -0
- package/dist/chunks/remote-config-C2ytQwrs.js +525 -0
- package/dist/chunks/rtk-hook-core-Bl6gYHPN.js +133 -0
- package/dist/chunks/rtk-resolver-CLo-gZzw.js +21 -0
- package/dist/chunks/rtk-resolver-CMQAidlI.js +2 -0
- package/dist/chunks/runtime-paths-Ci_ksSkj.js +75 -0
- package/dist/chunks/runtime-paths-JLrZ3wyi.js +2 -0
- package/dist/chunks/sandbox-deps-Dd5-kZnf.js +67 -0
- package/dist/chunks/session-Cez8aIWg.js +164 -0
- package/dist/chunks/spawn-command-Cc40FiZI.js +134 -0
- package/dist/chunks/visions-cli-BXQFIBCX.js +98 -0
- package/dist/chunks/visions-setup-CvVnOe1r.js +1753 -0
- package/dist/client/assets/favicon-CKygeLwW.svg +7 -0
- package/dist/client/assets/index-B1MFijjb.css +1 -0
- package/dist/client/assets/index-CU1iPPdn.js +249 -0
- package/dist/client/index.html +108 -0
- package/dist/package.json +3 -0
- package/dist/scripts/postinstall-path-check.js +36 -0
- package/dist/server/index.js +92 -0
- package/dist/session/command-hook-relay.js +16 -0
- package/dist/session/hook-relay.js +24 -0
- package/dist/session/hook-tool-relay.js +107 -0
- package/dist/session/statusline-relay.js +82 -0
- package/dist/session/visions-relay.js +627 -0
- package/dist/tools/vscode-visions/extension.js +289 -0
- package/dist/tools/vscode-visions/lsp-convert.js +80 -0
- package/dist/tools/vscode-visions/package.json +32 -0
- package/dist/tools/vscode-visions/visions-lsp-core.js +112 -0
- package/package.json +96 -4
- package/scripts/postinstall.mjs +4 -0
- package/scripts/prepare-build.js +38 -0
- package/scripts/recover-handoff.mjs +82 -0
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
//#region server/core/node-pty-preflight-core.ts
|
|
5
|
+
var REPAIR_COMMAND = "npm rebuild -g node-pty --allow-scripts=node-pty";
|
|
6
|
+
var FALLBACK_REPAIR_COMMAND = "cd \"$(npm root -g)/glimmervoid\" && npm rebuild node-pty --dangerously-allow-all-scripts";
|
|
7
|
+
var CHECKOUT_REPAIR_COMMAND = "npm rebuild node-pty --dangerously-allow-all-scripts";
|
|
8
|
+
function nativeBindingCandidates({ packageDir, platform, arch }) {
|
|
9
|
+
return [
|
|
10
|
+
["build", "Release"],
|
|
11
|
+
["build", "Debug"],
|
|
12
|
+
["prebuilds", `${platform}-${arch}`]
|
|
13
|
+
].flatMap((searchDir) => [path.join(packageDir, ...searchDir, "pty.node"), path.join(packageDir, "lib", ...searchDir, "pty.node")]);
|
|
14
|
+
}
|
|
15
|
+
function spawnHelperCandidates({ packageDir, platform, arch }) {
|
|
16
|
+
if (platform !== "darwin") return [];
|
|
17
|
+
return nativeBindingCandidates({
|
|
18
|
+
packageDir,
|
|
19
|
+
platform,
|
|
20
|
+
arch
|
|
21
|
+
}).map((bindingPath) => path.join(path.dirname(bindingPath), "spawn-helper"));
|
|
22
|
+
}
|
|
23
|
+
function nativeToolchainHint(platform) {
|
|
24
|
+
if (platform === "linux") return "install build tools: sudo apt install build-essential python3";
|
|
25
|
+
if (platform === "win32") return "install Visual Studio Build Tools";
|
|
26
|
+
if (platform === "darwin") return "install Xcode Command Line Tools: xcode-select --install";
|
|
27
|
+
return "install the native build tools for this platform";
|
|
28
|
+
}
|
|
29
|
+
function nodePtyRebuildHint(platform) {
|
|
30
|
+
return `${nativeToolchainHint(platform)}; then rebuild. global install: ${REPAIR_COMMAND} (the flag is required on npm 12, unknown-but-harmless on older npm). clone or source checkout, from the checkout root: ${CHECKOUT_REPAIR_COMMAND}`;
|
|
31
|
+
}
|
|
32
|
+
function formatNodePtyBootRefusal({ platform, packageDir, reason }) {
|
|
33
|
+
return [
|
|
34
|
+
"Refusing to start: the node-pty native binding did not load, so Glimmervoid cannot spawn a terminal for any session.",
|
|
35
|
+
` reason: ${reason}`,
|
|
36
|
+
` node-pty package: ${packageDir}`,
|
|
37
|
+
" npm 12 blocks dependency install scripts by default, so a global install from a git spec leaves node-pty uncompiled.",
|
|
38
|
+
` repair (global install): ${REPAIR_COMMAND}`,
|
|
39
|
+
" (run it from a directory outside the installed package; from inside it the same command is project-scoped and dies with EALLOWSCRIPTS)",
|
|
40
|
+
" (name node-pty, not glimmervoid: rebuilding the wrapper exits 0 and rebuilds nothing nested)",
|
|
41
|
+
` fallback (global install): ${FALLBACK_REPAIR_COMMAND}`,
|
|
42
|
+
` repair (clone or source checkout): ${CHECKOUT_REPAIR_COMMAND}`,
|
|
43
|
+
" (run it from the checkout root; the global repair above rebuilds nothing in a checkout and exits 0)",
|
|
44
|
+
` ${nativeToolchainHint(platform)}`,
|
|
45
|
+
" Run \"glimmervoid doctor\" for the same check plus the rest of your install."
|
|
46
|
+
].join("\n");
|
|
47
|
+
}
|
|
48
|
+
//#endregion
|
|
49
|
+
//#region server/core/text-core.ts
|
|
50
|
+
var OTHER_CATEGORY_RE = /\p{C}+/gu;
|
|
51
|
+
function firstLine(text) {
|
|
52
|
+
return String(text ?? "").split(/\r?\n/)[0].trim();
|
|
53
|
+
}
|
|
54
|
+
function sanitizeOneLine(raw, maxChars) {
|
|
55
|
+
return String(raw ?? "").replace(OTHER_CATEGORY_RE, " ").replace(/\s+/g, " ").trim().slice(0, maxChars).trim();
|
|
56
|
+
}
|
|
57
|
+
//#endregion
|
|
58
|
+
//#region server/node-pty-preflight.ts
|
|
59
|
+
function hostScope() {
|
|
60
|
+
return {
|
|
61
|
+
platform: process.platform,
|
|
62
|
+
arch: process.arch
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
function errorMessage(error) {
|
|
66
|
+
return firstLine(error instanceof Error ? error.message : String(error));
|
|
67
|
+
}
|
|
68
|
+
function resolveNodePtyPackageDir() {
|
|
69
|
+
try {
|
|
70
|
+
return { packageDir: path.dirname(createRequire(import.meta.url).resolve("node-pty/package.json")) };
|
|
71
|
+
} catch (err) {
|
|
72
|
+
return { error: errorMessage(err) };
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
var EXECUTABLE_MODE = 493;
|
|
76
|
+
function isExecutableByThisProcess(helperPath) {
|
|
77
|
+
try {
|
|
78
|
+
fs.accessSync(helperPath, fs.constants.X_OK);
|
|
79
|
+
return true;
|
|
80
|
+
} catch {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
function ensureSpawnHelperExecutable(packageDir, scope) {
|
|
85
|
+
const helperCandidates = spawnHelperCandidates({
|
|
86
|
+
packageDir,
|
|
87
|
+
...scope
|
|
88
|
+
});
|
|
89
|
+
if (helperCandidates.length === 0) return { ok: true };
|
|
90
|
+
let hasExecutableHelper = false;
|
|
91
|
+
let lastRepairError = "no candidate spawn-helper exists";
|
|
92
|
+
for (const helperPath of helperCandidates) {
|
|
93
|
+
if (isExecutableByThisProcess(helperPath)) {
|
|
94
|
+
hasExecutableHelper = true;
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
if (!fs.existsSync(helperPath)) continue;
|
|
98
|
+
try {
|
|
99
|
+
fs.chmodSync(helperPath, EXECUTABLE_MODE);
|
|
100
|
+
} catch (err) {
|
|
101
|
+
lastRepairError = `${helperPath}: ${errorMessage(err)}`;
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
if (isExecutableByThisProcess(helperPath)) {
|
|
105
|
+
hasExecutableHelper = true;
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
lastRepairError = `${helperPath}: still not executable after chmod 0755`;
|
|
109
|
+
}
|
|
110
|
+
if (hasExecutableHelper) return { ok: true };
|
|
111
|
+
return {
|
|
112
|
+
ok: false,
|
|
113
|
+
reason: `no executable spawn-helper found; checked ${helperCandidates.join(", ")}; last repair failure: ${lastRepairError}`
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
function scanNativeBinding(packageDir, scope = hostScope()) {
|
|
117
|
+
const candidates = nativeBindingCandidates({
|
|
118
|
+
packageDir,
|
|
119
|
+
...scope
|
|
120
|
+
});
|
|
121
|
+
if (!candidates.some((candidate) => fs.existsSync(candidate))) return {
|
|
122
|
+
ok: false,
|
|
123
|
+
reason: `no pty.node found; checked ${candidates.join(", ")}`,
|
|
124
|
+
packageDir
|
|
125
|
+
};
|
|
126
|
+
const helper = ensureSpawnHelperExecutable(packageDir, scope);
|
|
127
|
+
if (!helper.ok) return {
|
|
128
|
+
ok: false,
|
|
129
|
+
reason: helper.reason,
|
|
130
|
+
packageDir
|
|
131
|
+
};
|
|
132
|
+
return {
|
|
133
|
+
ok: true,
|
|
134
|
+
packageDir
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
async function probeNodePty() {
|
|
138
|
+
const resolved = resolveNodePtyPackageDir();
|
|
139
|
+
if ("error" in resolved) return {
|
|
140
|
+
ok: false,
|
|
141
|
+
reason: resolved.error,
|
|
142
|
+
packageDir: "(unresolved)"
|
|
143
|
+
};
|
|
144
|
+
try {
|
|
145
|
+
await import("node-pty");
|
|
146
|
+
} catch (err) {
|
|
147
|
+
return {
|
|
148
|
+
ok: false,
|
|
149
|
+
reason: errorMessage(err),
|
|
150
|
+
packageDir: resolved.packageDir
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
return scanNativeBinding(resolved.packageDir);
|
|
154
|
+
}
|
|
155
|
+
function requireExecutableSpawnHelper(packageDir = null, scope = hostScope()) {
|
|
156
|
+
const resolved = packageDir === null ? resolveNodePtyPackageDir() : { packageDir };
|
|
157
|
+
if ("error" in resolved) return;
|
|
158
|
+
const helper = ensureSpawnHelperExecutable(resolved.packageDir, scope);
|
|
159
|
+
if (!helper.ok) throw new Error(helper.reason);
|
|
160
|
+
}
|
|
161
|
+
//#endregion
|
|
162
|
+
export { formatNodePtyBootRefusal as a, sanitizeOneLine as i, requireExecutableSpawnHelper as n, nodePtyRebuildHint as o, firstLine as r, probeNodePty as t };
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { l as resolveConfigPath } from "./config-store-mm7MgzoL.js";
|
|
2
|
+
import { c as defaultPairingsPath, i as REVOCATION_PROPAGATION_SECONDS, l as defaultSeenPath, n as normalizeRemoteConfig, o as createPairingsStore, s as createSeenStore } from "./remote-config-C2ytQwrs.js";
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
//#region server/text-format.ts
|
|
5
|
+
function trimIso(iso) {
|
|
6
|
+
return iso.replace("T", " ").slice(0, 19);
|
|
7
|
+
}
|
|
8
|
+
function formatTimestamp(value) {
|
|
9
|
+
if (typeof value === "number" && Number.isFinite(value)) return trimIso(new Date(value).toISOString());
|
|
10
|
+
if (typeof value === "string") return trimIso(value);
|
|
11
|
+
return "-";
|
|
12
|
+
}
|
|
13
|
+
//#endregion
|
|
14
|
+
//#region server/pair-cli.ts
|
|
15
|
+
var PASSWORD_WARNING = [
|
|
16
|
+
"Treat this link like a password. Anyone who opens it gets full control of this",
|
|
17
|
+
"machine through Glimmervoid (it can start Claude Code sessions in any directory).",
|
|
18
|
+
"It works once and expires in 10 minutes."
|
|
19
|
+
].join("\n");
|
|
20
|
+
function readConfig(configPath) {
|
|
21
|
+
try {
|
|
22
|
+
return JSON.parse(fs.readFileSync(configPath, "utf8"));
|
|
23
|
+
} catch (err) {
|
|
24
|
+
console.error(`Could not read ${configPath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function argValue(args, flag) {
|
|
29
|
+
const idx = args.indexOf(flag);
|
|
30
|
+
if (idx === -1 || idx + 1 >= args.length) return null;
|
|
31
|
+
return args[idx + 1];
|
|
32
|
+
}
|
|
33
|
+
function buildPairUrl(remote, token) {
|
|
34
|
+
if (remote.publicHost) return `https://${remote.publicHost}/pair/${token}`;
|
|
35
|
+
return `http://127.0.0.1:${remote.port}/pair/${token}`;
|
|
36
|
+
}
|
|
37
|
+
function runMint(store, remote, name) {
|
|
38
|
+
if (!remote.publicHost && !remote.port) {
|
|
39
|
+
console.error("remote.port is not set, so a pairing link cannot be built.");
|
|
40
|
+
console.error("Set remote.port (and remote.publicHost for a shareable link) in config.json first.");
|
|
41
|
+
return 1;
|
|
42
|
+
}
|
|
43
|
+
const minted = store.mintPending({ name: name || "" });
|
|
44
|
+
if (!minted) {
|
|
45
|
+
console.error("Failed to write the pairing file - no token was created.");
|
|
46
|
+
return 1;
|
|
47
|
+
}
|
|
48
|
+
console.log("");
|
|
49
|
+
console.log(` ${buildPairUrl(remote, minted.token)}`);
|
|
50
|
+
console.log("");
|
|
51
|
+
console.log(PASSWORD_WARNING);
|
|
52
|
+
if (!remote.publicHost) {
|
|
53
|
+
console.log("");
|
|
54
|
+
console.log("remote.publicHost is not set, so the link above points at loopback.");
|
|
55
|
+
console.log("Set it to the hostname your reverse proxy serves to get a shareable link.");
|
|
56
|
+
}
|
|
57
|
+
if (!remote.enabled) {
|
|
58
|
+
console.log("");
|
|
59
|
+
console.log("Note: remote.enabled is false, so the remote listener is not running yet.");
|
|
60
|
+
}
|
|
61
|
+
return 0;
|
|
62
|
+
}
|
|
63
|
+
function runList(store, seenStore) {
|
|
64
|
+
const devices = store.listDevices();
|
|
65
|
+
if (devices.length === 0) {
|
|
66
|
+
console.log("No paired devices.");
|
|
67
|
+
return 0;
|
|
68
|
+
}
|
|
69
|
+
const seen = seenStore.readAll();
|
|
70
|
+
console.log(`${"ID".padEnd(14)}${"NAME".padEnd(22)}${"PAIRED".padEnd(21)}${"LAST SEEN".padEnd(21)}STATUS`);
|
|
71
|
+
for (const device of devices) {
|
|
72
|
+
const status = device.revokedAt ? `revoked ${formatTimestamp(device.revokedAt)}` : "active";
|
|
73
|
+
console.log(device.id.padEnd(14) + (device.name || "-").padEnd(22) + formatTimestamp(device.createdAt).padEnd(21) + formatTimestamp(seen[device.id]).padEnd(21) + status);
|
|
74
|
+
}
|
|
75
|
+
return 0;
|
|
76
|
+
}
|
|
77
|
+
function runRevoke(store, id) {
|
|
78
|
+
const outcome = store.revokeDevice(id);
|
|
79
|
+
if (outcome.ok) {
|
|
80
|
+
console.log(`Revoked ${id}. A running Glimmervoid applies this within ${REVOCATION_PROPAGATION_SECONDS} seconds, no restart needed.`);
|
|
81
|
+
return 0;
|
|
82
|
+
}
|
|
83
|
+
if (outcome.reason === "unknown") {
|
|
84
|
+
console.error(`No paired device with id ${id}. Run "glimmervoid pair --list" to see the ids.`);
|
|
85
|
+
return 1;
|
|
86
|
+
}
|
|
87
|
+
console.error("Failed to write the pairing file - nothing was revoked.");
|
|
88
|
+
return 1;
|
|
89
|
+
}
|
|
90
|
+
function runPairCli(args) {
|
|
91
|
+
const configPath = resolveConfigPath();
|
|
92
|
+
const config = readConfig(configPath);
|
|
93
|
+
if (!config) return 1;
|
|
94
|
+
const remote = normalizeRemoteConfig(config.remote);
|
|
95
|
+
const store = createPairingsStore({ filePath: defaultPairingsPath(configPath) });
|
|
96
|
+
const seenStore = createSeenStore({ filePath: defaultSeenPath(configPath) });
|
|
97
|
+
if (args.includes("--list")) return runList(store, seenStore);
|
|
98
|
+
if (args.includes("--revoke")) {
|
|
99
|
+
const id = argValue(args, "--revoke");
|
|
100
|
+
if (!id) {
|
|
101
|
+
console.error("Usage: glimmervoid pair --revoke <device-id>");
|
|
102
|
+
return 1;
|
|
103
|
+
}
|
|
104
|
+
return runRevoke(store, id);
|
|
105
|
+
}
|
|
106
|
+
store.prunePending();
|
|
107
|
+
return runMint(store, remote, argValue(args, "--name"));
|
|
108
|
+
}
|
|
109
|
+
//#endregion
|
|
110
|
+
export { runPairCli };
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
//#region bin/path-doctor.ts
|
|
3
|
+
function isWin(platform) {
|
|
4
|
+
return platform === "win32";
|
|
5
|
+
}
|
|
6
|
+
function normalizeDir(dir, platform) {
|
|
7
|
+
if (!dir) return "";
|
|
8
|
+
let d = String(dir).trim();
|
|
9
|
+
if (d.length >= 2 && d.startsWith("\"") && d.endsWith("\"")) d = d.slice(1, -1);
|
|
10
|
+
d = d.replace(/[\\/]+$/, "");
|
|
11
|
+
if (d === "") return "";
|
|
12
|
+
return isWin(platform) ? d.toLowerCase() : d;
|
|
13
|
+
}
|
|
14
|
+
function onPath(dir, { pathEnv, platform } = {}) {
|
|
15
|
+
const target = normalizeDir(dir, platform);
|
|
16
|
+
if (!target) return false;
|
|
17
|
+
const sep = isWin(platform) ? ";" : ":";
|
|
18
|
+
for (const entry of String(pathEnv || "").split(sep)) if (normalizeDir(entry, platform) === target) return true;
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
function npmGlobalBinDir({ env = {}, platform, homedir, resolvedPrefix } = {}) {
|
|
22
|
+
const prefix = env.npm_config_prefix || resolvedPrefix || null;
|
|
23
|
+
if (isWin(platform)) {
|
|
24
|
+
if (prefix) return prefix;
|
|
25
|
+
if (homedir) return path.join(homedir, "AppData", "Roaming", "npm");
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
if (prefix) return path.join(prefix, "bin");
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
function pnpmGlobalBinDir({ env = {}, platform, homedir } = {}) {
|
|
32
|
+
if (env.PNPM_HOME) return env.PNPM_HOME;
|
|
33
|
+
if (!homedir) return null;
|
|
34
|
+
if (isWin(platform)) return path.join(homedir, "AppData", "Local", "pnpm");
|
|
35
|
+
return path.join(homedir, ".local", "share", "pnpm");
|
|
36
|
+
}
|
|
37
|
+
function formatPathNotice({ installedBinDir, onPathFlag, platform } = {}) {
|
|
38
|
+
const dir = installedBinDir || "(unknown)";
|
|
39
|
+
if (onPathFlag) return [
|
|
40
|
+
"glimmervoid installed. Its command directory is on your PATH:",
|
|
41
|
+
` ${dir}`,
|
|
42
|
+
"Run \"glimmervoid\" to start, or \"glimmervoid doctor\" to check your setup."
|
|
43
|
+
].join("\n");
|
|
44
|
+
const lines = [
|
|
45
|
+
"glimmervoid was installed, but its command directory is NOT on your PATH:",
|
|
46
|
+
` ${dir}`,
|
|
47
|
+
"That is why typing \"glimmervoid\" is not recognized yet. To fix it:"
|
|
48
|
+
];
|
|
49
|
+
if (isWin(platform)) {
|
|
50
|
+
lines.push(" In PowerShell (adds it to your user PATH, permanent):");
|
|
51
|
+
lines.push(` [Environment]::SetEnvironmentVariable("PATH", [Environment]::GetEnvironmentVariable("PATH","User") + ";${dir}", "User")`);
|
|
52
|
+
lines.push(" Then open a NEW terminal and run \"glimmervoid\".");
|
|
53
|
+
lines.push(" (Reinstalling Node.js from the official installer also adds this directory for you.)");
|
|
54
|
+
}
|
|
55
|
+
if (!isWin(platform)) {
|
|
56
|
+
lines.push(` Add to your shell profile: export PATH="$PATH:${dir}"`);
|
|
57
|
+
lines.push(" Then open a new terminal and run \"glimmervoid\".");
|
|
58
|
+
}
|
|
59
|
+
lines.push("See the README \"Troubleshooting\" section for more, including pnpm setups.");
|
|
60
|
+
return lines.join("\n");
|
|
61
|
+
}
|
|
62
|
+
//#endregion
|
|
63
|
+
export { pnpmGlobalBinDir as i, npmGlobalBinDir as n, onPath as r, formatPathNotice as t };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
//#region shared/contracts/refocus.ts
|
|
3
|
+
var SessionStartHookResponse = z.object({ hookSpecificOutput: z.object({
|
|
4
|
+
hookEventName: z.literal("SessionStart"),
|
|
5
|
+
additionalContext: z.string()
|
|
6
|
+
}) });
|
|
7
|
+
//#endregion
|
|
8
|
+
//#region session/core/refocus-core.ts
|
|
9
|
+
function buildRefocusReminder({ taskTitle, latestPlanTitle }) {
|
|
10
|
+
const intentParts = [];
|
|
11
|
+
if (taskTitle?.trim()) intentParts.push(`Current task: "${taskTitle}".`);
|
|
12
|
+
if (latestPlanTitle?.trim()) intentParts.push(`Latest plan: "${latestPlanTitle}".`);
|
|
13
|
+
if (intentParts.length === 0) return null;
|
|
14
|
+
return `Context was just compacted. ${intentParts.join(" ")} Before your next step, check it still serves this intent and drop work that does not.`;
|
|
15
|
+
}
|
|
16
|
+
function refocusReplyFor({ accepted, event, payload, readContext }) {
|
|
17
|
+
if (!accepted) return null;
|
|
18
|
+
if (event.toLowerCase() !== "sessionstart") return null;
|
|
19
|
+
if (payload.source !== "compact") return null;
|
|
20
|
+
const additionalContext = buildRefocusReminder(readContext());
|
|
21
|
+
if (additionalContext === null) return null;
|
|
22
|
+
return { hookSpecificOutput: {
|
|
23
|
+
hookEventName: "SessionStart",
|
|
24
|
+
additionalContext
|
|
25
|
+
} };
|
|
26
|
+
}
|
|
27
|
+
function sessionStartContextOutput(body) {
|
|
28
|
+
if (body === null) return null;
|
|
29
|
+
try {
|
|
30
|
+
const parsedResponse = SessionStartHookResponse.safeParse(JSON.parse(body));
|
|
31
|
+
if (!parsedResponse.success) return null;
|
|
32
|
+
return JSON.stringify(parsedResponse.data);
|
|
33
|
+
} catch {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
//#endregion
|
|
38
|
+
export { sessionStartContextOutput as n, refocusReplyFor as t };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
//#region session/relay-stdin.ts
|
|
2
|
+
function readStdin(stream) {
|
|
3
|
+
return new Promise((resolve) => {
|
|
4
|
+
const chunks = [];
|
|
5
|
+
let done = false;
|
|
6
|
+
const finish = () => {
|
|
7
|
+
if (done) return;
|
|
8
|
+
done = true;
|
|
9
|
+
resolve(Buffer.concat(chunks));
|
|
10
|
+
};
|
|
11
|
+
stream.on("data", (chunk) => {
|
|
12
|
+
chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : Buffer.from(chunk));
|
|
13
|
+
});
|
|
14
|
+
stream.on("end", finish);
|
|
15
|
+
stream.on("error", finish);
|
|
16
|
+
stream.on("close", finish);
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
//#endregion
|
|
20
|
+
export { readStdin as t };
|