diff --git a/package.json b/package.json index 86ccfec3..a7057914 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,8 @@ "test:parser": "npx tsx src/sdk/parser.test.ts", "test:context": "echo '{\"session_id\":\"test-'$(date +%s)'\",\"cwd\":\"'$(pwd)'\",\"source\":\"startup\"}' | node plugin/scripts/context-hook.js 2>/dev/null", "test:context:verbose": "echo '{\"session_id\":\"test-'$(date +%s)'\",\"cwd\":\"'$(pwd)'\",\"source\":\"startup\"}' | node plugin/scripts/context-hook.js", - "sync-marketplace": "rsync -av --delete --exclude=.git ./ ~/.claude/plugins/marketplaces/thedotmack/ && cd ~/.claude/plugins/marketplaces/thedotmack/ && npm install", + "sync-marketplace": "node scripts/sync-marketplace.cjs", + "sync-marketplace:force": "node scripts/sync-marketplace.cjs --force", "worker:start": "pm2 start ecosystem.config.cjs", "worker:stop": "pm2 stop claude-mem-worker", "worker:restart": "pm2 restart claude-mem-worker", diff --git a/plugin/hooks/hooks.json b/plugin/hooks/hooks.json index b3f3c2bd..99a3a8e0 100644 --- a/plugin/hooks/hooks.json +++ b/plugin/hooks/hooks.json @@ -8,12 +8,12 @@ { "type": "command", "command": "node \"${CLAUDE_PLUGIN_ROOT}/../scripts/smart-install.js\" && node ${CLAUDE_PLUGIN_ROOT}/scripts/context-hook.js", - "timeout": 5 + "timeout": 300 }, { "type": "command", "command": "node ${CLAUDE_PLUGIN_ROOT}/scripts/user-message-hook.js", - "timeout": 5 + "timeout": 10 } ] } diff --git a/plugin/scripts/cleanup-hook.js b/plugin/scripts/cleanup-hook.js index de1cce26..509ad10e 100755 --- a/plugin/scripts/cleanup-hook.js +++ b/plugin/scripts/cleanup-hook.js @@ -1,5 +1,11 @@ #!/usr/bin/env node -import{stdin as S}from"process";import h from"path";import{homedir as y}from"os";import{join as o,dirname as C,basename as v}from"path";import{homedir as g}from"os";import{fileURLToPath as D}from"url";function M(){return typeof __dirname<"u"?__dirname:C(D(import.meta.url))}var H=M(),s=process.env.CLAUDE_MEM_DATA_DIR||o(g(),".claude-mem"),l=process.env.CLAUDE_CONFIG_DIR||o(g(),".claude"),X=o(s,"archives"),B=o(s,"logs"),V=o(s,"trash"),$=o(s,"backups"),j=o(s,"settings.json"),G=o(s,"claude-mem.db"),K=o(s,"vector-db"),Y=o(l,"settings.json"),J=o(l,"commands"),q=o(l,"CLAUDE.md");import{readFileSync as R,existsSync as U}from"fs";var N=["bugfix","feature","refactor","discovery","decision","change"],L=["how-it-works","why-it-exists","what-changed","problem-solution","gotcha","pattern","trade-off"];var m=N.join(","),d=L.join(",");var p=class{static DEFAULTS={CLAUDE_MEM_MODEL:"claude-haiku-4-5",CLAUDE_MEM_CONTEXT_OBSERVATIONS:"50",CLAUDE_MEM_WORKER_PORT:"37777",CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS:"true",CLAUDE_MEM_CONTEXT_SHOW_WORK_TOKENS:"true",CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_AMOUNT:"true",CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_PERCENT:"true",CLAUDE_MEM_CONTEXT_OBSERVATION_TYPES:m,CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS:d,CLAUDE_MEM_CONTEXT_FULL_COUNT:"5",CLAUDE_MEM_CONTEXT_FULL_FIELD:"narrative",CLAUDE_MEM_CONTEXT_SESSION_COUNT:"10",CLAUDE_MEM_CONTEXT_SHOW_LAST_SUMMARY:"true",CLAUDE_MEM_CONTEXT_SHOW_LAST_MESSAGE:"false"};static getAllDefaults(){return{...this.DEFAULTS}}static get(t){return process.env[t]||this.DEFAULTS[t]}static getInt(t){let r=this.get(t);return parseInt(r,10)}static getBool(t){return this.get(t)==="true"}static loadFromFile(t){if(!U(t))return this.getAllDefaults();let r=R(t,"utf-8"),n=JSON.parse(r).env||{},i={...this.DEFAULTS};for(let _ of Object.keys(this.DEFAULTS))n[_]!==void 0&&(i[_]=n[_]);return i}};function O(){let e=h.join(y(),".claude-mem","settings.json"),t=p.loadFromFile(e);return parseInt(t.CLAUDE_MEM_WORKER_PORT,10)}import{appendFileSync as I}from"fs";import{homedir as x}from"os";import{join as P}from"path";var k=P(x(),".claude-mem","silent.log");function c(e,t,r=""){let a=new Date().toISOString(),u=((new Error().stack||"").split(` -`)[2]||"").match(/at\s+(?:.*\s+)?\(?([^:]+):(\d+):(\d+)\)?/),A=u?`${u[1].split("/").pop()}:${u[2]}`:"unknown",E=`[${a}] [${A}] ${e}`;if(t!==void 0)try{E+=` ${JSON.stringify(t)}`}catch(T){E+=` [stringify error: ${T}]`}E+=` -`;try{I(k,E)}catch(T){console.error("[silent-debug] Failed to write to log:",T)}return r}async function f(e){c("[cleanup-hook] Hook fired",{session_id:e?.session_id,cwd:e?.cwd,reason:e?.reason}),e||(console.log("No input provided - this script is designed to run as a Claude Code SessionEnd hook"),console.log(` -Expected input format:`),console.log(JSON.stringify({session_id:"string",cwd:"string",transcript_path:"string",hook_event_name:"SessionEnd",reason:"exit"},null,2)),process.exit(0));let{session_id:t,reason:r}=e,a=O();try{let n=await fetch(`http://127.0.0.1:${a}/api/sessions/complete`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({claudeSessionId:t,reason:r}),signal:AbortSignal.timeout(2e3)});if(n.ok){let i=await n.json();c("[cleanup-hook] Session cleanup completed",i)}else c("[cleanup-hook] Session not found or already cleaned up")}catch(n){c("[cleanup-hook] Worker not reachable (non-critical)",{error:n.message})}console.log('{"continue": true, "suppressOutput": true}'),process.exit(0)}if(S.isTTY)f(void 0);else{let e="";S.on("data",t=>e+=t),S.on("end",async()=>{let t=e?JSON.parse(e):void 0;await f(t)})} +import{stdin as d}from"process";import m from"path";import{homedir as k}from"os";import{spawnSync as w}from"child_process";import{join as r,dirname as L,basename as j}from"path";import{homedir as O}from"os";import{fileURLToPath as R}from"url";function U(){return typeof __dirname<"u"?__dirname:L(R(import.meta.url))}var h=U(),a=process.env.CLAUDE_MEM_DATA_DIR||r(O(),".claude-mem"),S=process.env.CLAUDE_CONFIG_DIR||r(O(),".claude"),J=r(a,"archives"),q=r(a,"logs"),z=r(a,"trash"),Q=r(a,"backups"),Z=r(a,"settings.json"),tt=r(a,"claude-mem.db"),et=r(a,"vector-db"),ot=r(S,"settings.json"),rt=r(S,"commands"),nt=r(S,"CLAUDE.md");function g(){return r(h,"..","..")}import{readFileSync as x,existsSync as P}from"fs";var y=["bugfix","feature","refactor","discovery","decision","change"],I=["how-it-works","why-it-exists","what-changed","problem-solution","gotcha","pattern","trade-off"];var f=y.join(","),A=I.join(",");var u=class{static DEFAULTS={CLAUDE_MEM_MODEL:"claude-haiku-4-5",CLAUDE_MEM_CONTEXT_OBSERVATIONS:"50",CLAUDE_MEM_WORKER_PORT:"37777",CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS:"true",CLAUDE_MEM_CONTEXT_SHOW_WORK_TOKENS:"true",CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_AMOUNT:"true",CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_PERCENT:"true",CLAUDE_MEM_CONTEXT_OBSERVATION_TYPES:f,CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS:A,CLAUDE_MEM_CONTEXT_FULL_COUNT:"5",CLAUDE_MEM_CONTEXT_FULL_FIELD:"narrative",CLAUDE_MEM_CONTEXT_SESSION_COUNT:"10",CLAUDE_MEM_CONTEXT_SHOW_LAST_SUMMARY:"true",CLAUDE_MEM_CONTEXT_SHOW_LAST_MESSAGE:"false"};static getAllDefaults(){return{...this.DEFAULTS}}static get(t){return process.env[t]||this.DEFAULTS[t]}static getInt(t){let o=this.get(t);return parseInt(o,10)}static getBool(t){return this.get(t)==="true"}static loadFromFile(t){if(!P(t))return this.getAllDefaults();let o=x(t,"utf-8"),n=JSON.parse(o).env||{},s={...this.DEFAULTS};for(let c of Object.keys(this.DEFAULTS))n[c]!==void 0&&(s[c]=n[c]);return s}};var v=100,b=500,W=10;function T(){let e=m.join(k(),".claude-mem","settings.json"),t=u.loadFromFile(e);return parseInt(t.CLAUDE_MEM_WORKER_PORT,10)}async function C(){try{let e=T();return(await fetch(`http://127.0.0.1:${e}/health`,{signal:AbortSignal.timeout(v)})).ok}catch{return!1}}async function F(){try{let e=g(),t=m.join(e,"ecosystem.config.cjs");if(!existsSync(t))throw new Error(`Ecosystem config not found at ${t}`);let o=m.join(e,"node_modules",".bin","pm2"),i=process.platform==="win32"?o+".cmd":o,n=existsSync(i)?i:"pm2",s=w(n,["start",t],{cwd:e,stdio:"pipe",encoding:"utf-8",windowsHide:!0});if(s.status!==0)throw new Error(s.stderr||"PM2 start failed");for(let c=0;csetTimeout(_,b)),await C())return!0;return!1}catch{return!1}}async function D(){if(await C())return;if(!await F()){let t=T(),o=g();throw new Error(`Worker service failed to start on port ${t}. + +To start manually, run: + cd ${o} + npx pm2 start ecosystem.config.cjs + +If already running, try: npx pm2 restart claude-mem-worker`)}}import{appendFileSync as H}from"fs";import{homedir as X}from"os";import{join as B}from"path";var V=B(X(),".claude-mem","silent.log");function E(e,t,o=""){let i=new Date().toISOString(),_=((new Error().stack||"").split(` +`)[2]||"").match(/at\s+(?:.*\s+)?\(?([^:]+):(\d+):(\d+)\)?/),N=_?`${_[1].split("/").pop()}:${_[2]}`:"unknown",p=`[${i}] [${N}] ${e}`;if(t!==void 0)try{p+=` ${JSON.stringify(t)}`}catch(l){p+=` [stringify error: ${l}]`}p+=` +`;try{H(V,p)}catch(l){console.error("[silent-debug] Failed to write to log:",l)}return o}async function M(e){E("[cleanup-hook] Hook fired",{session_id:e?.session_id,cwd:e?.cwd,reason:e?.reason}),e||(console.log("No input provided - this script is designed to run as a Claude Code SessionEnd hook"),console.log(` +Expected input format:`),console.log(JSON.stringify({session_id:"string",cwd:"string",transcript_path:"string",hook_event_name:"SessionEnd",reason:"exit"},null,2)),process.exit(0));let{session_id:t,reason:o}=e;await D();let i=T();try{let n=await fetch(`http://127.0.0.1:${i}/api/sessions/complete`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({claudeSessionId:t,reason:o}),signal:AbortSignal.timeout(2e3)});if(n.ok){let s=await n.json();E("[cleanup-hook] Session cleanup completed",s)}else E("[cleanup-hook] Session not found or already cleaned up")}catch(n){E("[cleanup-hook] Worker not reachable (non-critical)",{error:n.message})}console.log('{"continue": true, "suppressOutput": true}'),process.exit(0)}if(d.isTTY)M(void 0);else{let e="";d.on("data",t=>e+=t),d.on("end",async()=>{let t=e?JSON.parse(e):void 0;await M(t)})} diff --git a/plugin/scripts/user-message-hook.js b/plugin/scripts/user-message-hook.js index 9d4187b0..bcc0848b 100755 --- a/plugin/scripts/user-message-hook.js +++ b/plugin/scripts/user-message-hook.js @@ -1,5 +1,11 @@ #!/usr/bin/env node -import{join as O,basename as x}from"path";import{homedir as P}from"os";import{existsSync as k}from"fs";import I from"path";import{homedir as w}from"os";import{join as e,dirname as M,basename as X}from"path";import{homedir as l}from"os";import{fileURLToPath as h}from"url";function N(){return typeof __dirname<"u"?__dirname:M(h(import.meta.url))}var G=N(),s=process.env.CLAUDE_MEM_DATA_DIR||e(l(),".claude-mem"),E=process.env.CLAUDE_CONFIG_DIR||e(l(),".claude"),K=e(s,"archives"),Y=e(s,"logs"),$=e(s,"trash"),q=e(s,"backups"),J=e(s,"settings.json"),Z=e(s,"claude-mem.db"),z=e(s,"vector-db"),Q=e(E,"settings.json"),tt=e(E,"commands"),et=e(E,"CLAUDE.md");import{readFileSync as R,existsSync as y}from"fs";var U=["bugfix","feature","refactor","discovery","decision","change"],L=["how-it-works","why-it-exists","what-changed","problem-solution","gotcha","pattern","trade-off"];var S=U.join(","),d=L.join(",");var c=class{static DEFAULTS={CLAUDE_MEM_MODEL:"claude-haiku-4-5",CLAUDE_MEM_CONTEXT_OBSERVATIONS:"50",CLAUDE_MEM_WORKER_PORT:"37777",CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS:"true",CLAUDE_MEM_CONTEXT_SHOW_WORK_TOKENS:"true",CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_AMOUNT:"true",CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_PERCENT:"true",CLAUDE_MEM_CONTEXT_OBSERVATION_TYPES:S,CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS:d,CLAUDE_MEM_CONTEXT_FULL_COUNT:"5",CLAUDE_MEM_CONTEXT_FULL_FIELD:"narrative",CLAUDE_MEM_CONTEXT_SESSION_COUNT:"10",CLAUDE_MEM_CONTEXT_SHOW_LAST_SUMMARY:"true",CLAUDE_MEM_CONTEXT_SHOW_LAST_MESSAGE:"false"};static getAllDefaults(){return{...this.DEFAULTS}}static get(t){return process.env[t]||this.DEFAULTS[t]}static getInt(t){let r=this.get(t);return parseInt(r,10)}static getBool(t){return this.get(t)==="true"}static loadFromFile(t){if(!y(t))return this.getAllDefaults();let r=R(t,"utf-8"),o=JSON.parse(r).env||{},a={...this.DEFAULTS};for(let i of Object.keys(this.DEFAULTS))o[i]!==void 0&&(a[i]=o[i]);return a}};function g(){let n=I.join(w(),".claude-mem","settings.json"),t=c.loadFromFile(n);return parseInt(t.CLAUDE_MEM_WORKER_PORT,10)}var v=O(P(),".claude","plugins","marketplaces","thedotmack"),b=O(v,"node_modules");k(b)||(console.error(` +import{join as D,basename as V}from"path";import{homedir as B}from"os";import{existsSync as j}from"fs";import l from"path";import{homedir as v}from"os";import{spawnSync as b}from"child_process";import{join as r,dirname as L,basename as J}from"path";import{homedir as g}from"os";import{fileURLToPath as R}from"url";function y(){return typeof __dirname<"u"?__dirname:L(R(import.meta.url))}var I=y(),a=process.env.CLAUDE_MEM_DATA_DIR||r(g(),".claude-mem"),p=process.env.CLAUDE_CONFIG_DIR||r(g(),".claude"),tt=r(a,"archives"),et=r(a,"logs"),ot=r(a,"trash"),rt=r(a,"backups"),nt=r(a,"settings.json"),st=r(a,"claude-mem.db"),it=r(a,"vector-db"),at=r(p,"settings.json"),ct=r(p,"commands"),_t=r(p,"CLAUDE.md");function m(){return r(I,"..","..")}import{readFileSync as P,existsSync as k}from"fs";var w=["bugfix","feature","refactor","discovery","decision","change"],x=["how-it-works","why-it-exists","what-changed","problem-solution","gotcha","pattern","trade-off"];var O=w.join(","),f=x.join(",");var E=class{static DEFAULTS={CLAUDE_MEM_MODEL:"claude-haiku-4-5",CLAUDE_MEM_CONTEXT_OBSERVATIONS:"50",CLAUDE_MEM_WORKER_PORT:"37777",CLAUDE_MEM_CONTEXT_SHOW_READ_TOKENS:"true",CLAUDE_MEM_CONTEXT_SHOW_WORK_TOKENS:"true",CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_AMOUNT:"true",CLAUDE_MEM_CONTEXT_SHOW_SAVINGS_PERCENT:"true",CLAUDE_MEM_CONTEXT_OBSERVATION_TYPES:O,CLAUDE_MEM_CONTEXT_OBSERVATION_CONCEPTS:f,CLAUDE_MEM_CONTEXT_FULL_COUNT:"5",CLAUDE_MEM_CONTEXT_FULL_FIELD:"narrative",CLAUDE_MEM_CONTEXT_SESSION_COUNT:"10",CLAUDE_MEM_CONTEXT_SHOW_LAST_SUMMARY:"true",CLAUDE_MEM_CONTEXT_SHOW_LAST_MESSAGE:"false"};static getAllDefaults(){return{...this.DEFAULTS}}static get(t){return process.env[t]||this.DEFAULTS[t]}static getInt(t){let o=this.get(t);return parseInt(o,10)}static getBool(t){return this.get(t)==="true"}static loadFromFile(t){if(!k(t))return this.getAllDefaults();let o=P(t,"utf-8"),n=JSON.parse(o).env||{},i={...this.DEFAULTS};for(let s of Object.keys(this.DEFAULTS))n[s]!==void 0&&(i[s]=n[s]);return i}};var W=100,H=500,F=10;function u(){let e=l.join(v(),".claude-mem","settings.json"),t=E.loadFromFile(e);return parseInt(t.CLAUDE_MEM_WORKER_PORT,10)}async function A(){try{let e=u();return(await fetch(`http://127.0.0.1:${e}/health`,{signal:AbortSignal.timeout(W)})).ok}catch{return!1}}async function X(){try{let e=m(),t=l.join(e,"ecosystem.config.cjs");if(!existsSync(t))throw new Error(`Ecosystem config not found at ${t}`);let o=l.join(e,"node_modules",".bin","pm2"),c=process.platform==="win32"?o+".cmd":o,n=existsSync(c)?c:"pm2",i=b(n,["start",t],{cwd:e,stdio:"pipe",encoding:"utf-8",windowsHide:!0});if(i.status!==0)throw new Error(i.stderr||"PM2 start failed");for(let s=0;ssetTimeout(_,H)),await A())return!0;return!1}catch{return!1}}async function C(){if(await A())return;if(!await X()){let t=u(),o=m();throw new Error(`Worker service failed to start on port ${t}. + +To start manually, run: + cd ${o} + npx pm2 start ecosystem.config.cjs + +If already running, try: npx pm2 restart claude-mem-worker`)}}var G=D(B(),".claude","plugins","marketplaces","thedotmack"),K=D(G,"node_modules");j(K)||(console.error(` --- \u{1F389} Note: This appears under Plugin Hook Error, but it's not an error. That's the only option for user messages in Claude Code UI until a better method is provided. @@ -17,7 +23,7 @@ Dependencies have been installed in the background. This only happens once. Thank you for installing Claude-Mem! This message was not added to your startup context, so you can continue working as normal. -`),process.exit(3));try{let n=g(),t=x(process.cwd()),r=await fetch(`http://127.0.0.1:${n}/api/context/inject?project=${encodeURIComponent(t)}&colors=true`,{method:"GET",signal:AbortSignal.timeout(5e3)});if(!r.ok)throw new Error(`Worker error ${r.status}`);let u=await r.text(),o=new Date,a=new Date("2025-12-06T00:00:00Z"),i=new Date("2025-12-05T05:00:00Z"),T="";o=1&&m<=5,D=p>=17&&p<19;C&&D?_=` +`);let T="";if(n=1&&d<=5,U=S>=17&&S<19;N&&U?T=` \u{1F534} LIVE NOW: AMA w/ Dev (@thedotmack) until 7pm EST -`:_=` +`:T=` \u2013 LIVE AMA w/ Dev (@thedotmack) Dec 1st\u20135th, 5pm to 7pm EST `}console.error(` \u{1F4DD} Claude-Mem Context Loaded \u2139\uFE0F Note: This appears as stderr but is informational only -`+u+` +`+c+` \u{1F4A1} New! Wrap all or part of any message with ... to prevent storing sensitive information in your observation history. -\u{1F4AC} Community https://discord.gg/J4wttp9vDu`+T+_+` -\u{1F4FA} Watch live in browser http://localhost:${n}/ -`)}catch(n){console.error(`\u274C Failed to load context display: ${n}`)}process.exit(3); +\u{1F4AC} Community https://discord.gg/J4wttp9vDu`+_+T+` +\u{1F4FA} Watch live in browser http://localhost:${e}/ +`)}catch(e){console.error(`\u274C Failed to load context display: ${e}`)}process.exit(3); diff --git a/scripts/smart-install.js b/scripts/smart-install.js index 84df81bd..26988faf 100644 --- a/scripts/smart-install.js +++ b/scripts/smart-install.js @@ -387,7 +387,7 @@ async function main() { } catch (error) { // Worker might already be running or PM2 not available - that's okay // The ensureWorkerRunning() function will handle auto-start when needed - log('ℹ️ Worker startup error', colors.dim); + log('ℹ️ Worker will start automatically when needed', colors.dim); } // Success - dependencies installed (if needed) diff --git a/scripts/sync-marketplace.cjs b/scripts/sync-marketplace.cjs index d0f8c9b6..856b9d74 100644 --- a/scripts/sync-marketplace.cjs +++ b/scripts/sync-marketplace.cjs @@ -7,11 +7,12 @@ */ const { execSync } = require('child_process'); -const { existsSync } = require('fs'); +const { existsSync, readFileSync } = require('fs'); const path = require('path'); const os = require('os'); const INSTALLED_PATH = path.join(os.homedir(), '.claude', 'plugins', 'marketplaces', 'thedotmack'); +const CACHE_BASE_PATH = path.join(os.homedir(), '.claude', 'plugins', 'cache', 'thedotmack', 'claude-mem'); function getCurrentBranch() { try { @@ -29,8 +30,9 @@ function getCurrentBranch() { } const branch = getCurrentBranch(); +const isForce = process.argv.includes('--force'); -if (branch && branch !== 'main') { +if (branch && branch !== 'main' && !isForce) { console.log(''); console.log('\x1b[33m%s\x1b[0m', `WARNING: Installed plugin is on beta branch: ${branch}`); console.log('\x1b[33m%s\x1b[0m', 'Running rsync would overwrite beta code.'); @@ -43,6 +45,18 @@ if (branch && branch !== 'main') { process.exit(1); } +// Get version from plugin.json +function getPluginVersion() { + try { + const pluginJsonPath = path.join(__dirname, '..', 'plugin', '.claude-plugin', 'plugin.json'); + const pluginJson = JSON.parse(readFileSync(pluginJsonPath, 'utf-8')); + return pluginJson.version; + } catch (error) { + console.error('\x1b[31m%s\x1b[0m', 'Failed to read plugin version:', error.message); + process.exit(1); + } +} + // Normal rsync for main branch or fresh install console.log('Syncing to marketplace...'); try { @@ -57,6 +71,16 @@ try { { stdio: 'inherit' } ); + // Sync to cache folder with version + const version = getPluginVersion(); + const CACHE_VERSION_PATH = path.join(CACHE_BASE_PATH, version); + + console.log(`Syncing to cache folder (version ${version})...`); + execSync( + `rsync -av --delete --exclude=.git plugin/ "${CACHE_VERSION_PATH}/"`, + { stdio: 'inherit' } + ); + console.log('\x1b[32m%s\x1b[0m', 'Sync complete!'); } catch (error) { console.error('\x1b[31m%s\x1b[0m', 'Sync failed:', error.message); diff --git a/src/hooks/cleanup-hook.ts b/src/hooks/cleanup-hook.ts index cfb43602..120da130 100644 --- a/src/hooks/cleanup-hook.ts +++ b/src/hooks/cleanup-hook.ts @@ -7,7 +7,7 @@ */ import { stdin } from 'process'; -import { getWorkerPort } from '../shared/worker-utils.js'; +import { ensureWorkerRunning, getWorkerPort } from '../shared/worker-utils.js'; import { silentDebug } from '../utils/silent-debug.js'; export interface SessionEndInput { @@ -44,6 +44,9 @@ async function cleanupHook(input?: SessionEndInput): Promise { const { session_id, reason } = input; + // Ensure worker is running + await ensureWorkerRunning(); + const port = getWorkerPort(); try { diff --git a/src/hooks/user-message-hook.ts b/src/hooks/user-message-hook.ts index 828c3b5b..3544d897 100644 --- a/src/hooks/user-message-hook.ts +++ b/src/hooks/user-message-hook.ts @@ -9,7 +9,7 @@ import { join, basename } from "path"; import { homedir } from "os"; import { existsSync } from "fs"; -import { getWorkerPort } from "../shared/worker-utils.js"; +import { ensureWorkerRunning, getWorkerPort } from "../shared/worker-utils.js"; // Check if node_modules exists - if not, this is first run const pluginDir = join(homedir(), '.claude', 'plugins', 'marketplaces', 'thedotmack'); @@ -40,6 +40,9 @@ This message was not added to your startup context, so you can continue working } try { + // Ensure worker is running + await ensureWorkerRunning(); + const port = getWorkerPort(); const project = basename(process.cwd());