codiedev 0.6.0 → 0.6.1

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.
@@ -76,6 +76,14 @@ function hasCursorMcpEntry() {
76
76
  return false;
77
77
  }
78
78
  }
79
+ // Match both legacy `npx codiedev-hook ...` and absolute-path forms
80
+ // `<node> <.../codiedev/dist/hook.js> ...` — installer switched to absolute
81
+ // paths in 0.6.1 to work in GUI-launched contexts where shell PATH is missing.
82
+ function isCodiedevHookCommand(cmd) {
83
+ if (!cmd)
84
+ return false;
85
+ return cmd.includes("codiedev-hook") || /codiedev[\\/]dist[\\/]hook/.test(cmd);
86
+ }
79
87
  // Cursor's hooks.json schema is { hooks: { sessionEnd: [{ command, ... }] } }
80
88
  // — flat array of objects with `command`, not the Claude/Codex nested
81
89
  // `{ hooks: [{ command }] }` wrapper.
@@ -88,10 +96,7 @@ function hasCursorHook() {
88
96
  const arr = parsed.hooks?.sessionEnd;
89
97
  if (!Array.isArray(arr))
90
98
  return false;
91
- return arr.some((h) => {
92
- const cmd = h.command;
93
- return (cmd ?? "").includes("codiedev-hook");
94
- });
99
+ return arr.some((h) => isCodiedevHookCommand(h.command));
95
100
  }
96
101
  catch {
97
102
  return false;
@@ -110,7 +115,7 @@ function hasCodiedevHook(settingsPath, hookKey) {
110
115
  const inner = h.hooks;
111
116
  if (!Array.isArray(inner))
112
117
  return false;
113
- return inner.some((x) => (x.command ?? "").includes("codiedev-hook"));
118
+ return inner.some((x) => isCodiedevHookCommand(x.command));
114
119
  });
115
120
  }
116
121
  catch {
package/dist/utils.js CHANGED
@@ -142,6 +142,36 @@ const CURSOR_HOOKS_PATH = path.join(CURSOR_DIR, "hooks.json");
142
142
  const CURSOR_MCP_PATH = path.join(CURSOR_DIR, "mcp.json");
143
143
  const CURSOR_RULES_DIR = path.join(CURSOR_DIR, "rules");
144
144
  const CURSOR_RULES_PATH = path.join(CURSOR_RULES_DIR, "codiedev.mdc");
145
+ // GUI-launched agents (Cursor.app, future JetBrains plugins) don't source the
146
+ // user's shell rc, so nvm-managed `npx` and `node` aren't on PATH. Resolve the
147
+ // absolute path to the current node binary and our hook.js at install time and
148
+ // write that into hook configs so execution doesn't depend on the spawned
149
+ // shell's PATH. Falls back to `npx` if we can't resolve dist/hook.js.
150
+ function shellQuote(s) {
151
+ return `'${s.replace(/'/g, `'\\''`)}'`;
152
+ }
153
+ function resolveHookCommand(subcommand) {
154
+ try {
155
+ const cliEntry = process.argv[1];
156
+ if (cliEntry) {
157
+ const realCli = fs.realpathSync(cliEntry);
158
+ const distDir = path.dirname(realCli);
159
+ const hookScript = path.join(distDir, "hook.js");
160
+ if (fs.existsSync(hookScript)) {
161
+ return `${shellQuote(process.execPath)} ${shellQuote(hookScript)} ${subcommand}`;
162
+ }
163
+ }
164
+ }
165
+ catch {
166
+ // Fall through to npx fallback.
167
+ }
168
+ return `npx codiedev-hook ${subcommand}`;
169
+ }
170
+ function isCodiedevHookCommand(cmd) {
171
+ if (!cmd)
172
+ return false;
173
+ return cmd.includes("codiedev-hook") || /codiedev[\\/]dist[\\/]hook/.test(cmd);
174
+ }
145
175
  function claudeCodeInstalled() {
146
176
  return fs.existsSync(CLAUDE_DIR);
147
177
  }
@@ -169,30 +199,27 @@ function installHook() {
169
199
  if (!hooks.SessionEnd) {
170
200
  hooks.SessionEnd = [];
171
201
  }
172
- const sessionEndHooks = hooks.SessionEnd;
173
- const alreadyInstalled = sessionEndHooks.some((hook) => {
174
- const matcher = hook.matcher;
175
- const hooks2 = hook.hooks;
176
- if (hooks2) {
177
- return hooks2.some((h) => {
178
- const cmd = h.command;
179
- return cmd && cmd.includes("codiedev-hook");
180
- });
202
+ // Drop any prior codiedev entries (legacy `npx` form or older absolute
203
+ // paths) before re-adding so connect re-runs upgrade the resolved binary
204
+ // path instead of leaving stale entries behind.
205
+ const existing = hooks.SessionEnd;
206
+ const filtered = existing.filter((hook) => {
207
+ const inner = hook.hooks;
208
+ if (Array.isArray(inner)) {
209
+ return !inner.some((h) => isCodiedevHookCommand(h.command));
181
210
  }
182
- return matcher && matcher.includes("codiedev-hook");
211
+ return !isCodiedevHookCommand(hook.matcher);
183
212
  });
184
- if (alreadyInstalled) {
185
- return;
186
- }
187
- sessionEndHooks.push({
213
+ filtered.push({
188
214
  matcher: ".*",
189
215
  hooks: [
190
216
  {
191
217
  type: "command",
192
- command: "npx codiedev-hook capture",
218
+ command: resolveHookCommand("capture"),
193
219
  },
194
220
  ],
195
221
  });
222
+ hooks.SessionEnd = filtered;
196
223
  if (!fs.existsSync(CLAUDE_DIR)) {
197
224
  fs.mkdirSync(CLAUDE_DIR, { recursive: true });
198
225
  }
@@ -469,28 +496,23 @@ function installCodexHook() {
469
496
  if (!hooks.Stop) {
470
497
  hooks.Stop = [];
471
498
  }
472
- const stopHooks = hooks.Stop;
473
- const alreadyInstalled = stopHooks.some((hook) => {
499
+ const existing = hooks.Stop;
500
+ const filtered = existing.filter((hook) => {
474
501
  const inner = hook.hooks;
475
- if (!inner)
476
- return false;
477
- return inner.some((h) => {
478
- const cmd = h.command;
479
- return cmd && cmd.includes("codiedev-hook");
480
- });
502
+ if (!Array.isArray(inner))
503
+ return true;
504
+ return !inner.some((h) => isCodiedevHookCommand(h.command));
481
505
  });
482
- if (alreadyInstalled) {
483
- return;
484
- }
485
- stopHooks.push({
506
+ filtered.push({
486
507
  hooks: [
487
508
  {
488
509
  type: "command",
489
- command: "npx codiedev-hook capture-codex",
510
+ command: resolveHookCommand("capture-codex"),
490
511
  timeout: 30,
491
512
  },
492
513
  ],
493
514
  });
515
+ hooks.Stop = filtered;
494
516
  if (!fs.existsSync(CODEX_DIR)) {
495
517
  fs.mkdirSync(CODEX_DIR, { recursive: true });
496
518
  }
@@ -524,19 +546,14 @@ function installCursorHook() {
524
546
  if (!hooks.sessionEnd) {
525
547
  hooks.sessionEnd = [];
526
548
  }
527
- const sessionEndHooks = hooks.sessionEnd;
528
- const alreadyInstalled = sessionEndHooks.some((h) => {
529
- const cmd = h.command;
530
- return cmd && cmd.includes("codiedev-hook");
531
- });
532
- if (alreadyInstalled) {
533
- return;
534
- }
535
- sessionEndHooks.push({
536
- command: "npx codiedev-hook capture-cursor",
549
+ const existing = hooks.sessionEnd;
550
+ const filtered = existing.filter((h) => !isCodiedevHookCommand(h.command));
551
+ filtered.push({
552
+ command: resolveHookCommand("capture-cursor"),
537
553
  type: "command",
538
554
  timeout: 30,
539
555
  });
556
+ hooks.sessionEnd = filtered;
540
557
  fs.writeFileSync(CURSOR_HOOKS_PATH, JSON.stringify(hooksFile, null, 2), "utf8");
541
558
  }
542
559
  function parseClaudeCodeStats(content) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codiedev",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "description": "Connect Claude Code, Codex, or Cursor to CodieDev for org-wide session capture and artifact collaboration",
5
5
  "bin": {
6
6
  "codiedev": "./dist/cli.js",