grounder 0.0.2 → 0.2.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 (60) hide show
  1. package/README.md +113 -40
  2. package/dist/agents/claude.d.ts +15 -0
  3. package/dist/agents/claude.d.ts.map +1 -1
  4. package/dist/agents/claude.js +225 -1
  5. package/dist/agents/claude.js.map +1 -1
  6. package/dist/agents/cursor-hook-input.d.ts +14 -0
  7. package/dist/agents/cursor-hook-input.d.ts.map +1 -0
  8. package/dist/agents/cursor-hook-input.js +80 -0
  9. package/dist/agents/cursor-hook-input.js.map +1 -0
  10. package/dist/agents/cursor.d.ts +11 -0
  11. package/dist/agents/cursor.d.ts.map +1 -1
  12. package/dist/agents/cursor.js +175 -1
  13. package/dist/agents/cursor.js.map +1 -1
  14. package/dist/agents/hook-runtime.d.ts +108 -0
  15. package/dist/agents/hook-runtime.d.ts.map +1 -0
  16. package/dist/agents/hook-runtime.js +260 -0
  17. package/dist/agents/hook-runtime.js.map +1 -0
  18. package/dist/agents/types.d.ts +6 -0
  19. package/dist/agents/types.d.ts.map +1 -1
  20. package/dist/cli.js +25 -1
  21. package/dist/cli.js.map +1 -1
  22. package/dist/commands/check.d.ts +13 -0
  23. package/dist/commands/check.d.ts.map +1 -0
  24. package/dist/commands/check.js +21 -0
  25. package/dist/commands/check.js.map +1 -0
  26. package/dist/commands/doctor.d.ts +9 -0
  27. package/dist/commands/doctor.d.ts.map +1 -0
  28. package/dist/commands/doctor.js +270 -0
  29. package/dist/commands/doctor.js.map +1 -0
  30. package/dist/commands/handoff/list.d.ts +9 -0
  31. package/dist/commands/handoff/list.d.ts.map +1 -1
  32. package/dist/commands/handoff/list.js +15 -4
  33. package/dist/commands/handoff/list.js.map +1 -1
  34. package/dist/commands/handoff/peek.d.ts +38 -0
  35. package/dist/commands/handoff/peek.d.ts.map +1 -0
  36. package/dist/commands/handoff/peek.js +120 -0
  37. package/dist/commands/handoff/peek.js.map +1 -0
  38. package/dist/commands/status.d.ts +7 -0
  39. package/dist/commands/status.d.ts.map +1 -0
  40. package/dist/commands/status.js +101 -0
  41. package/dist/commands/status.js.map +1 -0
  42. package/dist/commands/vault/init.d.ts +2 -0
  43. package/dist/commands/vault/init.d.ts.map +1 -1
  44. package/dist/commands/vault/init.js +28 -0
  45. package/dist/commands/vault/init.js.map +1 -1
  46. package/dist/util/frontmatter.d.ts +11 -0
  47. package/dist/util/frontmatter.d.ts.map +1 -0
  48. package/dist/util/frontmatter.js +69 -0
  49. package/dist/util/frontmatter.js.map +1 -0
  50. package/dist/util/merge-json.d.ts +15 -0
  51. package/dist/util/merge-json.d.ts.map +1 -0
  52. package/dist/util/merge-json.js +40 -0
  53. package/dist/util/merge-json.js.map +1 -0
  54. package/dist/vault/find-usable-handoff.d.ts +19 -0
  55. package/dist/vault/find-usable-handoff.d.ts.map +1 -0
  56. package/dist/vault/find-usable-handoff.js +28 -0
  57. package/dist/vault/find-usable-handoff.js.map +1 -0
  58. package/package.json +1 -1
  59. package/templates/agents/claude/commands/grounder-task.md +4 -4
  60. package/templates/agents/cursor/commands/grounder-task.md +4 -4
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cursor-hook-input.js","sourceRoot":"","sources":["../../src/agents/cursor-hook-input.ts"],"names":[],"mappings":"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"}
@@ -1,7 +1,18 @@
1
1
  import type { AgentAdapter } from "./types.js";
2
+ /**
3
+ * Canonical sessionStart command for Cursor (home-local runtime, not `npx`).
4
+ * Always includes `--json` so stdout matches Cursor's `additional_context` contract.
5
+ * @see {@link peekHookCommand}
6
+ */
7
+ export declare function cursorPeekHookCommand(homeDir?: string, extraArgs?: readonly string[]): string;
2
8
  export declare function cursorCommandsDir(homeDir?: string): string;
9
+ /** Absolute path to Cursor's shared hooks config (`~/.cursor/hooks.json`). */
10
+ export declare function cursorHooksJsonPath(homeDir?: string): string;
3
11
  export declare function grounderNoteCommandPath(homeDir?: string): string;
4
12
  export declare function grounderTaskHandoffCommandPath(homeDir?: string): string;
5
13
  export declare function grounderTaskCommandPath(homeDir?: string): string;
14
+ export declare function expectedArtifacts(homeDir?: string): string[];
15
+ /** Paths of hook config this adapter touches — currently just `hooks.json`. */
16
+ export declare function expectedHookArtifacts(homeDir?: string): string[];
6
17
  export declare const cursor: AgentAdapter;
7
18
  //# sourceMappingURL=cursor.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"cursor.d.ts","sourceRoot":"","sources":["../../src/agents/cursor.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACV,YAAY,EAIb,MAAM,YAAY,CAAC;AAOpB,wBAAgB,iBAAiB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED,wBAAgB,uBAAuB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAEhE;AAED,wBAAgB,8BAA8B,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAEvE;AAED,wBAAgB,uBAAuB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAEhE;AAkBD,eAAO,MAAM,MAAM,EAAE,YAgBpB,CAAC"}
1
+ {"version":3,"file":"cursor.d.ts","sourceRoot":"","sources":["../../src/agents/cursor.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EACV,YAAY,EAIb,MAAM,YAAY,CAAC;AAOpB;;;;GAIG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,CAAC,EAAE,MAAM,EAChB,SAAS,GAAE,SAAS,MAAM,EAAe,GACxC,MAAM,CAER;AAED,wBAAgB,iBAAiB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED,8EAA8E;AAC9E,wBAAgB,mBAAmB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAE5D;AAED,wBAAgB,uBAAuB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAEhE;AAED,wBAAgB,8BAA8B,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAEvE;AAED,wBAAgB,uBAAuB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAEhE;AAED,wBAAgB,iBAAiB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAE5D;AAED,+EAA+E;AAC/E,wBAAgB,qBAAqB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAEhE;AA2LD,eAAO,MAAM,MAAM,EAAE,YAqBpB,CAAC"}
@@ -1,14 +1,28 @@
1
- import { copyFile, mkdir } from "node:fs/promises";
1
+ import { copyFile, mkdir, readFile } from "node:fs/promises";
2
2
  import path from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
4
  import { resolveHomeDir } from "../connector/home.js";
5
5
  import { fileExists } from "../util/fs.js";
6
+ import { mergeJsonFile } from "../util/merge-json.js";
7
+ import { installHookRuntime, isGrounderPeekHookCommand, isHookRuntimeStale, peekHookCommand, } from "./hook-runtime.js";
6
8
  const pkgRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
7
9
  const templateDir = path.join(pkgRoot, "templates", "agents", "cursor", "commands");
8
10
  const COMMANDS = ["grounder-note.md", "grounder-task-handoff.md", "grounder-task.md"];
11
+ /**
12
+ * Canonical sessionStart command for Cursor (home-local runtime, not `npx`).
13
+ * Always includes `--json` so stdout matches Cursor's `additional_context` contract.
14
+ * @see {@link peekHookCommand}
15
+ */
16
+ export function cursorPeekHookCommand(homeDir, extraArgs = ["--json"]) {
17
+ return peekHookCommand(homeDir, extraArgs);
18
+ }
9
19
  export function cursorCommandsDir(homeDir) {
10
20
  return path.join(resolveHomeDir(homeDir), ".cursor", "commands");
11
21
  }
22
+ /** Absolute path to Cursor's shared hooks config (`~/.cursor/hooks.json`). */
23
+ export function cursorHooksJsonPath(homeDir) {
24
+ return path.join(resolveHomeDir(homeDir), ".cursor", "hooks.json");
25
+ }
12
26
  export function grounderNoteCommandPath(homeDir) {
13
27
  return path.join(cursorCommandsDir(homeDir), "grounder-note.md");
14
28
  }
@@ -18,6 +32,13 @@ export function grounderTaskHandoffCommandPath(homeDir) {
18
32
  export function grounderTaskCommandPath(homeDir) {
19
33
  return path.join(cursorCommandsDir(homeDir), "grounder-task.md");
20
34
  }
35
+ export function expectedArtifacts(homeDir) {
36
+ return COMMANDS.map((filename) => path.join(cursorCommandsDir(homeDir), filename));
37
+ }
38
+ /** Paths of hook config this adapter touches — currently just `hooks.json`. */
39
+ export function expectedHookArtifacts(homeDir) {
40
+ return [cursorHooksJsonPath(homeDir)];
41
+ }
21
42
  async function installCommand(filename, opts) {
22
43
  const dest = path.join(cursorCommandsDir(opts.homeDir), filename);
23
44
  const existed = await fileExists(dest);
@@ -28,12 +49,164 @@ async function installCommand(filename, opts) {
28
49
  await copyFile(path.join(templateDir, filename), dest);
29
50
  return { dest, status: existed ? "overwritten" : "created" };
30
51
  }
52
+ // ---------------------------------------------------------------------------
53
+ // Session-start hook install (~/.cursor/hooks.json)
54
+ //
55
+ // Cursor hooks.json is a shared JSON object. Grounder only owns one entry in
56
+ // `hooks.sessionStart`; unrelated keys (other hook events, version, etc.) must
57
+ // survive merge. Relevant shape after install (path varies by home / Node):
58
+ //
59
+ // {
60
+ // "version": 1,
61
+ // "hooks": {
62
+ // "sessionStart": [
63
+ // { "command": "'/path/to/node' '/path/to/.grounder/runtime/dist/cli.js' handoff peek --json" }
64
+ // ]
65
+ // }
66
+ // }
67
+ //
68
+ // Terminology used below:
69
+ // - hooks root → the whole hooks.json object
70
+ // - hooks → hooksRoot.hooks (map of event name → hook entries)
71
+ // - sessionStart → hooks.sessionStart (flat array of { command } objects)
72
+ // - hook entry → { command: string }
73
+ //
74
+ // Unlike Claude Code, Cursor has no matcher groups — sessionStart is a flat
75
+ // array. Idempotency: {@link isGrounderPeekHookCommand} (runtime path or legacy npx).
76
+ // ---------------------------------------------------------------------------
77
+ function findPeekHookIndex(sessionStart) {
78
+ return sessionStart.findIndex((item) => item &&
79
+ typeof item === "object" &&
80
+ !Array.isArray(item) &&
81
+ isGrounderPeekHookCommand(item.command));
82
+ }
83
+ /** Whether `hooks.json` already lists any Grounder peek command (for status labeling). */
84
+ async function peekHookHadGrounderEntry(filePath) {
85
+ try {
86
+ const parsed = JSON.parse(await readFile(filePath, "utf8"));
87
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
88
+ return false;
89
+ }
90
+ const hooks = parsed.hooks;
91
+ if (!hooks || typeof hooks !== "object" || Array.isArray(hooks)) {
92
+ return false;
93
+ }
94
+ const sessionStart = hooks.sessionStart;
95
+ return Array.isArray(sessionStart) && findPeekHookIndex(sessionStart) >= 0;
96
+ }
97
+ catch {
98
+ return false;
99
+ }
100
+ }
101
+ /**
102
+ * Skip only when the canonical command is already present *and* the runtime is
103
+ * current for the running grounder version/source. Legacy `npx` entries or a
104
+ * stale runtime (missing, or symlinked/copied from a different source) always
105
+ * refresh — no `--force` required to migrate or to pick up an upgrade.
106
+ */
107
+ async function peekHookUpToDate(filePath, homeDir) {
108
+ if (!(await fileExists(filePath))) {
109
+ return false;
110
+ }
111
+ if (await isHookRuntimeStale(homeDir)) {
112
+ return false;
113
+ }
114
+ try {
115
+ const parsed = JSON.parse(await readFile(filePath, "utf8"));
116
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
117
+ return false;
118
+ }
119
+ const hooks = parsed.hooks;
120
+ if (!hooks || typeof hooks !== "object" || Array.isArray(hooks)) {
121
+ return false;
122
+ }
123
+ const sessionStart = hooks.sessionStart;
124
+ if (!Array.isArray(sessionStart)) {
125
+ return false;
126
+ }
127
+ const idx = findPeekHookIndex(sessionStart);
128
+ if (idx < 0) {
129
+ return false;
130
+ }
131
+ const command = sessionStart[idx].command;
132
+ return command === cursorPeekHookCommand(homeDir);
133
+ }
134
+ catch {
135
+ return false;
136
+ }
137
+ }
138
+ /**
139
+ * Deep-merge Grounder's sessionStart hook into an existing hooks.json object.
140
+ *
141
+ * Preserves every key except the nested path it owns. Strategy:
142
+ * 1. If a Grounder peek hook already exists (runtime or legacy npx) → replace in place.
143
+ * 2. Else → append Grounder's entry to `hooks.sessionStart`.
144
+ *
145
+ * On a brand-new file (`isFreshFile`), also sets `version: 1` when absent.
146
+ * Existing files keep whatever `version` (or lack of it) they already have.
147
+ *
148
+ * @param current - Parsed hooks.json root (object). Other top-level keys untouched.
149
+ * @param isFreshFile - True when the file did not exist before this write
150
+ * @param homeDir - Home override for the canonical command path
151
+ * @returns New hooks object with `hooks.sessionStart` updated
152
+ */
153
+ function mergeCursorHooks(current, isFreshFile, homeDir) {
154
+ const next = { ...current };
155
+ if (isFreshFile && next.version === undefined) {
156
+ next.version = 1;
157
+ }
158
+ const hooks = next.hooks && typeof next.hooks === "object" && !Array.isArray(next.hooks)
159
+ ? { ...next.hooks }
160
+ : {};
161
+ const existing = Array.isArray(hooks.sessionStart) ? [...hooks.sessionStart] : [];
162
+ const entry = { command: cursorPeekHookCommand(homeDir) };
163
+ const idx = findPeekHookIndex(existing);
164
+ if (idx >= 0) {
165
+ // Path 1: refresh existing Grounder hook entry in place
166
+ existing[idx] = entry;
167
+ }
168
+ else {
169
+ // Path 2: append — first install, or file had other sessionStart hooks only
170
+ existing.push(entry);
171
+ }
172
+ next.hooks = { ...hooks, sessionStart: existing };
173
+ return next;
174
+ }
175
+ /**
176
+ * Install (or refresh) Grounder's sessionStart teaser hook into `~/.cursor/hooks.json`.
177
+ *
178
+ * Also materializes `~/.grounder/runtime` (see {@link installHookRuntime}).
179
+ *
180
+ * Force semantics:
181
+ * - Up-to-date canonical entry + fresh runtime and `force` false → skip
182
+ * - Otherwise → refresh runtime + merge host config
183
+ *
184
+ * Unparseable hooks.json: {@link mergeJsonFile} backs off and this throws (never clobbers).
185
+ */
186
+ async function installHooks(opts) {
187
+ const dest = cursorHooksJsonPath(opts.homeDir);
188
+ const upToDate = await peekHookUpToDate(dest, opts.homeDir);
189
+ if (upToDate && !opts.force) {
190
+ return { artifacts: { [dest]: "skipped" } };
191
+ }
192
+ await installHookRuntime({ homeDir: opts.homeDir });
193
+ const fileExisted = await fileExists(dest);
194
+ const hadGrounderEntry = fileExisted && (await peekHookHadGrounderEntry(dest));
195
+ const result = await mergeJsonFile(dest, (current) => mergeCursorHooks(current, !fileExisted, opts.homeDir));
196
+ if (!result.ok) {
197
+ throw new Error(result.message);
198
+ }
199
+ const status = hadGrounderEntry ? "overwritten" : "created";
200
+ return { artifacts: { [dest]: status } };
201
+ }
31
202
  export const cursor = {
32
203
  id: "cursor",
33
204
  name: "Cursor",
34
205
  async isInstalled() {
35
206
  return fileExists(path.join(resolveHomeDir(), ".cursor"));
36
207
  },
208
+ expectedArtifacts,
209
+ expectedHookArtifacts,
37
210
  async install(opts) {
38
211
  const artifacts = {};
39
212
  for (const filename of COMMANDS) {
@@ -42,5 +215,6 @@ export const cursor = {
42
215
  }
43
216
  return { artifacts };
44
217
  },
218
+ installHooks,
45
219
  };
46
220
  //# sourceMappingURL=cursor.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"cursor.js","sourceRoot":"","sources":["../../src/agents/cursor.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cursor.js","sourceRoot":"","sources":["../../src/agents/cursor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,108 @@
1
+ /**
2
+ * Home-local hook runtime (Issue 2 — replace `npx grounder` in session hooks).
3
+ *
4
+ * ## Why
5
+ * Cursor/Claude session hooks previously used `npx grounder handoff peek`.
6
+ * Modern `npx` ignores a global/local install and fetches `grounder@latest` from
7
+ * the registry, so contributors (and anyone ahead of the last publish) get the
8
+ * wrong binary. Global `pnpm link` / `pnpm add -g` does not fix `npx`.
9
+ *
10
+ * ## Design
11
+ * On `vault init --hooks`, materialize this package's `dist/` at
12
+ * `~/.grounder/runtime/dist/` and point host hook configs at:
13
+ * `process.execPath` + `~/.grounder/runtime/dist/cli.js` + `handoff peek …`
14
+ * No global install and no registry fetch at hook time.
15
+ *
16
+ * How it's materialized depends on where grounder is running from:
17
+ * - **Durable source** (monorepo checkout, global install, linked devDependency) →
18
+ * **symlink** `dist/` straight to the source. `pnpm build` / upgrading the
19
+ * global install overwrites that same path in place, so the hook picks up
20
+ * new code immediately — no re-run of `vault init` ever needed.
21
+ * - **Ephemeral source** (bare `npx grounder …`, no install — each invocation
22
+ * resolves to an immutable, version-keyed npx cache dir that can be evicted
23
+ * or swapped out from under a symlink) → **copy** `dist/`. Re-run
24
+ * `grounder vault init <vault> --hooks` after upgrading to refresh; this is
25
+ * an inherent limitation of using npx with no install for something that
26
+ * must persist, not something we can engineer around.
27
+ *
28
+ * This mirrors husky/pre-commit hook shims: staying current is an explicit,
29
+ * idempotent step (never silent self-healing inside the hook itself — session
30
+ * hooks must stay fast and side-effect-free), and the step is a no-op unless
31
+ * something has actually changed.
32
+ *
33
+ * ## REVERT
34
+ * To restore the previous `npx grounder handoff peek` behavior:
35
+ * 1. Delete this file and its tests (`test/agents/hook-runtime.test.ts`).
36
+ * 2. In `cursor.ts` / `claude.ts`, set `*_PEEK_HOOK_COMMAND` back to
37
+ * `"npx grounder handoff peek"` (and drop calls to
38
+ * {@link installHookRuntime} / {@link peekHookCommand} /
39
+ * {@link isGrounderPeekHookCommand} / {@link isHookRuntimeStale}).
40
+ * 3. Remove runtime paths from `expectedHookArtifacts` and docs that mention
41
+ * `~/.grounder/runtime`.
42
+ */
43
+ import type { ArtifactStatus } from "./types.js";
44
+ /** Marker written next to the materialized runtime for debugging / staleness checks. */
45
+ export interface HookRuntimeManifest {
46
+ mode: "symlink" | "copy";
47
+ version: string;
48
+ nodePath: string;
49
+ sourcePackageRoot: string;
50
+ installedAt: string;
51
+ }
52
+ /** `~/.grounder/runtime` — private materialization of the CLI used only by session hooks. */
53
+ export declare function grounderRuntimeDir(homeDir?: string): string;
54
+ /** Absolute path to the materialized CLI entry (`…/runtime/dist/cli.js`). */
55
+ export declare function runtimeCliPath(homeDir?: string): string;
56
+ export declare function runtimeManifestPath(homeDir?: string): string;
57
+ /**
58
+ * POSIX-safe single-quote for embedding paths in hook `command` strings
59
+ * (Cursor/Claude run hooks via a shell on macOS/Linux and Git Bash on Windows).
60
+ */
61
+ export declare function shellQuote(value: string): string;
62
+ /**
63
+ * Build the sessionStart / SessionStart hook command for the home runtime.
64
+ *
65
+ * @param homeDir - Override home (tests / `GROUNDER_HOME`)
66
+ * @param extraArgs - Extra CLI args after `handoff peek` (e.g. `--json` for Cursor)
67
+ */
68
+ export declare function peekHookCommand(homeDir?: string, extraArgs?: readonly string[]): string;
69
+ /**
70
+ * True when `command` is Grounder's peek hook — current home-runtime form or
71
+ * legacy `npx grounder handoff peek` (with optional trailing flags).
72
+ * Used so upgrades replace the old entry instead of appending a duplicate.
73
+ */
74
+ export declare function isGrounderPeekHookCommand(command: unknown): boolean;
75
+ /**
76
+ * True when the materialized runtime is missing or out of date for the given
77
+ * source package:
78
+ * - **Symlink mode** (durable source): stale iff `dist/` isn't currently a
79
+ * symlink resolving to this source's `dist/` (cheap `realpath` compare — no
80
+ * staleness window, since a matching symlink is *always* current).
81
+ * - **Copy mode** (ephemeral `npx` source): stale iff the manifest is
82
+ * missing/unreadable or its recorded version differs from this source's.
83
+ *
84
+ * @param packageRoot - Source package root to compare against (defaults to the
85
+ * currently running package — override only in tests)
86
+ */
87
+ export declare function isHookRuntimeStale(homeDir?: string, packageRoot?: string): Promise<boolean>;
88
+ /**
89
+ * Materialize this package's `dist/` at `~/.grounder/runtime/dist/` — symlinked
90
+ * when the source is durable, copied when it's an ephemeral `npx` cache — and
91
+ * write a manifest recording how.
92
+ *
93
+ * Callers should gate on {@link isHookRuntimeStale} (or `force`) before calling —
94
+ * this always replaces whatever is currently at the destination, staging the
95
+ * new materialization first so a failed symlink/cp leaves the live runtime intact.
96
+ *
97
+ * @param options.packageRoot - Source package root to materialize (defaults to
98
+ * the currently running package — override only in tests)
99
+ */
100
+ export declare function installHookRuntime(options: {
101
+ homeDir?: string;
102
+ packageRoot?: string;
103
+ }): Promise<{
104
+ cliPath: string;
105
+ status: ArtifactStatus;
106
+ mode: "symlink" | "copy";
107
+ }>;
108
+ //# sourceMappingURL=hook-runtime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hook-runtime.d.ts","sourceRoot":"","sources":["../../src/agents/hook-runtime.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AAkBH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAKjD,wFAAwF;AACxF,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,SAAS,GAAG,MAAM,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,6FAA6F;AAC7F,wBAAgB,kBAAkB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAE3D;AAED,6EAA6E;AAC7E,wBAAgB,cAAc,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED,wBAAgB,mBAAmB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAE5D;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,SAAS,GAAE,SAAS,MAAM,EAAO,GAAG,MAAM,CAS3F;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAanE;AA4DD;;;;;;;;;;;GAWG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,CAAC,EAAE,MAAM,EAChB,WAAW,GAAE,MAA2B,GACvC,OAAO,CAAC,OAAO,CAAC,CAkBlB;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,kBAAkB,CAAC,OAAO,EAAE;IAChD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,GAAG,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,cAAc,CAAC;IAAC,IAAI,EAAE,SAAS,GAAG,MAAM,CAAA;CAAE,CAAC,CAmEjF"}
@@ -0,0 +1,260 @@
1
+ /**
2
+ * Home-local hook runtime (Issue 2 — replace `npx grounder` in session hooks).
3
+ *
4
+ * ## Why
5
+ * Cursor/Claude session hooks previously used `npx grounder handoff peek`.
6
+ * Modern `npx` ignores a global/local install and fetches `grounder@latest` from
7
+ * the registry, so contributors (and anyone ahead of the last publish) get the
8
+ * wrong binary. Global `pnpm link` / `pnpm add -g` does not fix `npx`.
9
+ *
10
+ * ## Design
11
+ * On `vault init --hooks`, materialize this package's `dist/` at
12
+ * `~/.grounder/runtime/dist/` and point host hook configs at:
13
+ * `process.execPath` + `~/.grounder/runtime/dist/cli.js` + `handoff peek …`
14
+ * No global install and no registry fetch at hook time.
15
+ *
16
+ * How it's materialized depends on where grounder is running from:
17
+ * - **Durable source** (monorepo checkout, global install, linked devDependency) →
18
+ * **symlink** `dist/` straight to the source. `pnpm build` / upgrading the
19
+ * global install overwrites that same path in place, so the hook picks up
20
+ * new code immediately — no re-run of `vault init` ever needed.
21
+ * - **Ephemeral source** (bare `npx grounder …`, no install — each invocation
22
+ * resolves to an immutable, version-keyed npx cache dir that can be evicted
23
+ * or swapped out from under a symlink) → **copy** `dist/`. Re-run
24
+ * `grounder vault init <vault> --hooks` after upgrading to refresh; this is
25
+ * an inherent limitation of using npx with no install for something that
26
+ * must persist, not something we can engineer around.
27
+ *
28
+ * This mirrors husky/pre-commit hook shims: staying current is an explicit,
29
+ * idempotent step (never silent self-healing inside the hook itself — session
30
+ * hooks must stay fast and side-effect-free), and the step is a no-op unless
31
+ * something has actually changed.
32
+ *
33
+ * ## REVERT
34
+ * To restore the previous `npx grounder handoff peek` behavior:
35
+ * 1. Delete this file and its tests (`test/agents/hook-runtime.test.ts`).
36
+ * 2. In `cursor.ts` / `claude.ts`, set `*_PEEK_HOOK_COMMAND` back to
37
+ * `"npx grounder handoff peek"` (and drop calls to
38
+ * {@link installHookRuntime} / {@link peekHookCommand} /
39
+ * {@link isGrounderPeekHookCommand} / {@link isHookRuntimeStale}).
40
+ * 3. Remove runtime paths from `expectedHookArtifacts` and docs that mention
41
+ * `~/.grounder/runtime`.
42
+ */
43
+ import { cp, lstat, mkdir, readFile, realpath, rename, rm, symlink, writeFile, } from "node:fs/promises";
44
+ import os from "node:os";
45
+ import path from "node:path";
46
+ import { fileURLToPath } from "node:url";
47
+ import { resolveHomeDir } from "../connector/home.js";
48
+ import { fileExists } from "../util/fs.js";
49
+ /** Package root (`packages/grounder`) when running from `dist/agents/`. */
50
+ const defaultPackageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
51
+ /** `~/.grounder/runtime` — private materialization of the CLI used only by session hooks. */
52
+ export function grounderRuntimeDir(homeDir) {
53
+ return path.join(resolveHomeDir(homeDir), ".grounder", "runtime");
54
+ }
55
+ /** Absolute path to the materialized CLI entry (`…/runtime/dist/cli.js`). */
56
+ export function runtimeCliPath(homeDir) {
57
+ return path.join(grounderRuntimeDir(homeDir), "dist", "cli.js");
58
+ }
59
+ export function runtimeManifestPath(homeDir) {
60
+ return path.join(grounderRuntimeDir(homeDir), "manifest.json");
61
+ }
62
+ /**
63
+ * POSIX-safe single-quote for embedding paths in hook `command` strings
64
+ * (Cursor/Claude run hooks via a shell on macOS/Linux and Git Bash on Windows).
65
+ */
66
+ export function shellQuote(value) {
67
+ return `'${value.replace(/'/g, `'\\''`)}'`;
68
+ }
69
+ /**
70
+ * Build the sessionStart / SessionStart hook command for the home runtime.
71
+ *
72
+ * @param homeDir - Override home (tests / `GROUNDER_HOME`)
73
+ * @param extraArgs - Extra CLI args after `handoff peek` (e.g. `--json` for Cursor)
74
+ */
75
+ export function peekHookCommand(homeDir, extraArgs = []) {
76
+ const parts = [
77
+ shellQuote(process.execPath),
78
+ shellQuote(runtimeCliPath(homeDir)),
79
+ "handoff",
80
+ "peek",
81
+ ...extraArgs,
82
+ ];
83
+ return parts.join(" ");
84
+ }
85
+ /**
86
+ * True when `command` is Grounder's peek hook — current home-runtime form or
87
+ * legacy `npx grounder handoff peek` (with optional trailing flags).
88
+ * Used so upgrades replace the old entry instead of appending a duplicate.
89
+ */
90
+ export function isGrounderPeekHookCommand(command) {
91
+ if (typeof command !== "string") {
92
+ return false;
93
+ }
94
+ const trimmed = command.trim();
95
+ if (/^npx\s+grounder\s+handoff\s+peek(?:\s|$)/.test(trimmed)) {
96
+ return true;
97
+ }
98
+ // Normalize separators so Windows absolute paths still match.
99
+ const normalized = trimmed.replace(/\\/g, "/");
100
+ return (normalized.includes("/.grounder/runtime/dist/cli.js") && /\bhandoff\s+peek\b/.test(normalized));
101
+ }
102
+ /**
103
+ * Best-effort: is `root` inside a throwaway cache (npx / pnpm dlx), where
104
+ * content can be evicted or swapped for a *different* version at any time?
105
+ * Those sources can't be symlinked durably — copy instead.
106
+ */
107
+ function isEphemeralSource(root) {
108
+ const normalized = `${path.resolve(root).replace(/\\/g, "/")}/`;
109
+ const tmp = `${path.resolve(os.tmpdir()).replace(/\\/g, "/")}/`;
110
+ if (normalized.startsWith(tmp)) {
111
+ return true;
112
+ }
113
+ return /\/(_npx|\.npm\/_npx|\.pnpm-dlx-|pnpm-dlx-)[^/]*\//.test(normalized);
114
+ }
115
+ async function readPackageVersion(packageRoot) {
116
+ try {
117
+ const raw = JSON.parse(await readFile(path.join(packageRoot, "package.json"), "utf8"));
118
+ return typeof raw.version === "string" ? raw.version : "unknown";
119
+ }
120
+ catch {
121
+ return "unknown";
122
+ }
123
+ }
124
+ async function readRuntimeManifest(homeDir) {
125
+ try {
126
+ const raw = JSON.parse(await readFile(runtimeManifestPath(homeDir), "utf8"));
127
+ if (typeof raw.version !== "string" || (raw.mode !== "symlink" && raw.mode !== "copy")) {
128
+ return null;
129
+ }
130
+ return {
131
+ mode: raw.mode,
132
+ version: raw.version,
133
+ nodePath: typeof raw.nodePath === "string" ? raw.nodePath : "",
134
+ sourcePackageRoot: typeof raw.sourcePackageRoot === "string" ? raw.sourcePackageRoot : "",
135
+ installedAt: typeof raw.installedAt === "string" ? raw.installedAt : "",
136
+ };
137
+ }
138
+ catch {
139
+ return null;
140
+ }
141
+ }
142
+ /** Realpath of `destDist` if it's currently a directory symlink, else `null`. */
143
+ async function currentSymlinkTarget(destDist) {
144
+ try {
145
+ const info = await lstat(destDist);
146
+ if (!info.isSymbolicLink()) {
147
+ return null;
148
+ }
149
+ return await realpath(destDist);
150
+ }
151
+ catch {
152
+ return null;
153
+ }
154
+ }
155
+ /**
156
+ * True when the materialized runtime is missing or out of date for the given
157
+ * source package:
158
+ * - **Symlink mode** (durable source): stale iff `dist/` isn't currently a
159
+ * symlink resolving to this source's `dist/` (cheap `realpath` compare — no
160
+ * staleness window, since a matching symlink is *always* current).
161
+ * - **Copy mode** (ephemeral `npx` source): stale iff the manifest is
162
+ * missing/unreadable or its recorded version differs from this source's.
163
+ *
164
+ * @param packageRoot - Source package root to compare against (defaults to the
165
+ * currently running package — override only in tests)
166
+ */
167
+ export async function isHookRuntimeStale(homeDir, packageRoot = defaultPackageRoot) {
168
+ if (!(await fileExists(runtimeCliPath(homeDir)))) {
169
+ return true;
170
+ }
171
+ const destDist = path.join(grounderRuntimeDir(homeDir), "dist");
172
+ if (isEphemeralSource(packageRoot)) {
173
+ const manifest = await readRuntimeManifest(homeDir);
174
+ if (manifest?.mode !== "copy") {
175
+ return true;
176
+ }
177
+ return manifest.version !== (await readPackageVersion(packageRoot));
178
+ }
179
+ const sourceDistDir = path.join(packageRoot, "dist");
180
+ const resolvedSource = await realpath(sourceDistDir).catch(() => path.resolve(sourceDistDir));
181
+ const target = await currentSymlinkTarget(destDist);
182
+ return target !== resolvedSource;
183
+ }
184
+ /**
185
+ * Materialize this package's `dist/` at `~/.grounder/runtime/dist/` — symlinked
186
+ * when the source is durable, copied when it's an ephemeral `npx` cache — and
187
+ * write a manifest recording how.
188
+ *
189
+ * Callers should gate on {@link isHookRuntimeStale} (or `force`) before calling —
190
+ * this always replaces whatever is currently at the destination, staging the
191
+ * new materialization first so a failed symlink/cp leaves the live runtime intact.
192
+ *
193
+ * @param options.packageRoot - Source package root to materialize (defaults to
194
+ * the currently running package — override only in tests)
195
+ */
196
+ export async function installHookRuntime(options) {
197
+ const packageRoot = options.packageRoot ?? defaultPackageRoot;
198
+ const sourceDistDir = path.join(packageRoot, "dist");
199
+ const runtimeDir = grounderRuntimeDir(options.homeDir);
200
+ const destDist = path.join(runtimeDir, "dist");
201
+ const cliPath = runtimeCliPath(options.homeDir);
202
+ const existed = await fileExists(cliPath);
203
+ if (!(await fileExists(path.join(sourceDistDir, "cli.js")))) {
204
+ throw new Error(`Grounder dist missing at ${sourceDistDir}. Run \`pnpm build\` (or install a published package) before vault init --hooks.`);
205
+ }
206
+ await mkdir(runtimeDir, { recursive: true });
207
+ // Stage into a sibling path first, then swap. If symlink/cp fails, the live
208
+ // `dist/` (and any hooks pointing at it) stay intact.
209
+ const stagingDist = `${destDist}.staging`;
210
+ const backupDist = `${destDist}.bak`;
211
+ await rm(stagingDist, { recursive: true, force: true });
212
+ await rm(backupDist, { recursive: true, force: true });
213
+ const mode = isEphemeralSource(packageRoot) ? "copy" : "symlink";
214
+ try {
215
+ if (mode === "symlink") {
216
+ const resolvedSource = await realpath(sourceDistDir).catch(() => path.resolve(sourceDistDir));
217
+ await symlink(resolvedSource, stagingDist, process.platform === "win32" ? "junction" : "dir");
218
+ }
219
+ else {
220
+ await cp(sourceDistDir, stagingDist, { recursive: true, force: true });
221
+ }
222
+ // Move the live dest aside (if any), then promote staging. On promote
223
+ // failure, restore the backup so hooks keep a working cli.js.
224
+ try {
225
+ await rename(destDist, backupDist);
226
+ }
227
+ catch (error) {
228
+ if (error.code !== "ENOENT") {
229
+ throw error;
230
+ }
231
+ }
232
+ try {
233
+ await rename(stagingDist, destDist);
234
+ }
235
+ catch (error) {
236
+ try {
237
+ await rename(backupDist, destDist);
238
+ }
239
+ catch {
240
+ // Best-effort restore; rethrow the promote failure below.
241
+ }
242
+ throw error;
243
+ }
244
+ await rm(backupDist, { recursive: true, force: true });
245
+ }
246
+ catch (error) {
247
+ await rm(stagingDist, { recursive: true, force: true }).catch(() => undefined);
248
+ throw error;
249
+ }
250
+ const manifest = {
251
+ mode,
252
+ version: await readPackageVersion(packageRoot),
253
+ nodePath: process.execPath,
254
+ sourcePackageRoot: packageRoot,
255
+ installedAt: new Date().toISOString(),
256
+ };
257
+ await writeFile(runtimeManifestPath(options.homeDir), `${JSON.stringify(manifest, null, 2)}\n`);
258
+ return { cliPath, status: existed ? "overwritten" : "created", mode };
259
+ }
260
+ //# sourceMappingURL=hook-runtime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hook-runtime.js","sourceRoot":"","sources":["../../src/agents/hook-runtime.ts"],"names":[],"mappings":"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"}