@stackmemoryai/stackmemory 0.3.17 โ†’ 0.3.18

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 (212) hide show
  1. package/dist/cli/commands/skills.js +15 -2
  2. package/dist/cli/commands/skills.js.map +2 -2
  3. package/dist/cli/index.js +113 -834
  4. package/dist/cli/index.js.map +3 -3
  5. package/dist/core/context/dual-stack-manager.js +1 -1
  6. package/dist/core/context/dual-stack-manager.js.map +1 -1
  7. package/dist/core/context/frame-manager.js +3 -0
  8. package/dist/core/context/frame-manager.js.map +2 -2
  9. package/dist/integrations/claude-code/subagent-client.js +106 -3
  10. package/dist/integrations/claude-code/subagent-client.js.map +2 -2
  11. package/dist/servers/railway/config.js +51 -0
  12. package/dist/servers/railway/config.js.map +7 -0
  13. package/dist/servers/railway/index-enhanced.js +156 -0
  14. package/dist/servers/railway/index-enhanced.js.map +7 -0
  15. package/dist/servers/railway/minimal.js +48 -3
  16. package/dist/servers/railway/minimal.js.map +2 -2
  17. package/dist/servers/railway/storage-test.js +455 -0
  18. package/dist/servers/railway/storage-test.js.map +7 -0
  19. package/dist/skills/claude-skills.js +13 -12
  20. package/dist/skills/claude-skills.js.map +2 -2
  21. package/dist/skills/recursive-agent-orchestrator.js +27 -18
  22. package/dist/skills/recursive-agent-orchestrator.js.map +2 -2
  23. package/dist/skills/unified-rlm-orchestrator.js.map +2 -2
  24. package/package.json +6 -18
  25. package/scripts/README-TESTING.md +186 -0
  26. package/scripts/analyze-cli-security.js +288 -0
  27. package/scripts/archive/add-phase-tasks-to-linear.js +163 -0
  28. package/scripts/archive/analyze-linear-duplicates.js +214 -0
  29. package/scripts/archive/analyze-remaining-duplicates.js +230 -0
  30. package/scripts/archive/analyze-sta-duplicates.js +292 -0
  31. package/scripts/archive/analyze-sta-graphql.js +399 -0
  32. package/scripts/archive/cancel-duplicate-tasks.ts +246 -0
  33. package/scripts/archive/check-all-duplicates.ts +419 -0
  34. package/scripts/archive/clean-duplicate-tasks.js +114 -0
  35. package/scripts/archive/cleanup-duplicate-tasks.ts +286 -0
  36. package/scripts/archive/create-phase-tasks.js +387 -0
  37. package/scripts/archive/delete-linear-duplicates.js +182 -0
  38. package/scripts/archive/delete-remaining-duplicates.js +158 -0
  39. package/scripts/archive/delete-sta-duplicates.js +201 -0
  40. package/scripts/archive/delete-sta-oauth.js +201 -0
  41. package/scripts/archive/export-sta-tasks.js +62 -0
  42. package/scripts/archive/install-auto-sync.js +266 -0
  43. package/scripts/archive/install-chromadb-hooks.sh +133 -0
  44. package/scripts/archive/install-enhanced-clear-hooks.sh +431 -0
  45. package/scripts/archive/install-post-task-hooks.sh +289 -0
  46. package/scripts/archive/install-stackmemory-hooks.sh +420 -0
  47. package/scripts/archive/merge-linear-duplicates-safe.ts +362 -0
  48. package/scripts/archive/merge-linear-duplicates.ts +180 -0
  49. package/scripts/archive/remove-sta-tasks.js +70 -0
  50. package/scripts/archive/setup-background-sync.sh +168 -0
  51. package/scripts/archive/setup-claude-auto-triggers.sh +181 -0
  52. package/scripts/archive/setup-claude-autostart.sh +305 -0
  53. package/scripts/archive/setup-git-hooks.sh +25 -0
  54. package/scripts/archive/setup-linear-oauth.sh +46 -0
  55. package/scripts/archive/setup-mcp.sh +113 -0
  56. package/scripts/archive/setup-railway-deployment.sh +81 -0
  57. package/scripts/auto-handoff.sh +262 -0
  58. package/scripts/background-sync-manager.js +416 -0
  59. package/scripts/benchmark-performance.ts +57 -0
  60. package/scripts/check-redis.ts +48 -0
  61. package/scripts/chromadb-auto-loader.sh +128 -0
  62. package/scripts/chromadb-context-loader.js +479 -0
  63. package/scripts/claude-chromadb-hook.js +460 -0
  64. package/scripts/claude-code-wrapper.sh +66 -0
  65. package/scripts/claude-linear-skill.js +455 -0
  66. package/scripts/claude-pre-commit.sh +302 -0
  67. package/scripts/claude-sm-autostart.js +532 -0
  68. package/scripts/claude-sm-setup.sh +367 -0
  69. package/scripts/claude-with-chromadb.sh +69 -0
  70. package/scripts/claude-worktree-manager.sh +323 -0
  71. package/scripts/claude-worktree-monitor.sh +371 -0
  72. package/scripts/claude-worktree-setup.sh +327 -0
  73. package/scripts/clean-linear-backlog.js +273 -0
  74. package/scripts/cleanup-old-sessions.sh +57 -0
  75. package/scripts/codex-wrapper.sh +88 -0
  76. package/scripts/create-sandbox.sh +269 -0
  77. package/scripts/debug-linear-update.js +174 -0
  78. package/scripts/delete-linear-tasks.js +167 -0
  79. package/scripts/deploy.sh +89 -0
  80. package/scripts/deployment/railway.sh +352 -0
  81. package/scripts/deployment/test-deployment.js +194 -0
  82. package/scripts/detect-and-rehydrate.js +162 -0
  83. package/scripts/detect-and-rehydrate.mjs +165 -0
  84. package/scripts/development/create-demo-tasks.js +143 -0
  85. package/scripts/development/debug-frame-test.js +16 -0
  86. package/scripts/development/demo-auto-sync.js +128 -0
  87. package/scripts/development/fix-all-imports.js +213 -0
  88. package/scripts/development/fix-imports.js +229 -0
  89. package/scripts/development/fix-lint-loop.cjs +103 -0
  90. package/scripts/development/fix-project-id.ts +161 -0
  91. package/scripts/development/fix-strict-mode-issues.ts +291 -0
  92. package/scripts/development/reorganize-structure.sh +228 -0
  93. package/scripts/development/test-persistence-direct.js +148 -0
  94. package/scripts/development/test-persistence.js +114 -0
  95. package/scripts/development/test-tasks.js +93 -0
  96. package/scripts/development/update-imports.js +212 -0
  97. package/scripts/fetch-linear-status.js +125 -0
  98. package/scripts/git-hooks/README.md +310 -0
  99. package/scripts/git-hooks/branch-context-manager.sh +342 -0
  100. package/scripts/git-hooks/post-checkout-stackmemory.sh +63 -0
  101. package/scripts/git-hooks/post-commit-stackmemory.sh +305 -0
  102. package/scripts/git-hooks/pre-commit-stackmemory.sh +275 -0
  103. package/scripts/hooks/cleanup-shell.sh +130 -0
  104. package/scripts/hooks/task-complete.sh +114 -0
  105. package/scripts/initialize.ts +129 -0
  106. package/scripts/install-claude-hooks-auto.js +104 -0
  107. package/scripts/install-claude-hooks.sh +133 -0
  108. package/scripts/install-global.sh +296 -0
  109. package/scripts/install.sh +235 -0
  110. package/scripts/linear-auto-sync.js +262 -0
  111. package/scripts/linear-auto-sync.sh +161 -0
  112. package/scripts/linear-sync-daemon.js +150 -0
  113. package/scripts/linear-task-review.js +237 -0
  114. package/scripts/list-linear-tasks.ts +178 -0
  115. package/scripts/mcp-proxy.js +66 -0
  116. package/scripts/opencode-wrapper.sh +85 -0
  117. package/scripts/publish-local.js +74 -0
  118. package/scripts/query-chromadb.ts +201 -0
  119. package/scripts/railway-env-setup.sh +39 -0
  120. package/scripts/reconcile-local-tasks.js +170 -0
  121. package/scripts/recreate-frames-db.js +89 -0
  122. package/scripts/setup/claude-integration.js +138 -0
  123. package/scripts/setup/configure-alias.js +125 -0
  124. package/scripts/setup/configure-codex-alias.js +161 -0
  125. package/scripts/setup/configure-opencode-alias.js +175 -0
  126. package/scripts/setup-claude-integration.js +204 -0
  127. package/scripts/setup-claude-integration.sh +183 -0
  128. package/scripts/setup.sh +31 -0
  129. package/scripts/show-linear-summary.ts +172 -0
  130. package/scripts/stackmemory-auto-handoff.sh +231 -0
  131. package/scripts/stackmemory-daemon.sh +40 -0
  132. package/scripts/start-linear-sync-daemon.sh +141 -0
  133. package/scripts/start-temporal-paradox.sh +214 -0
  134. package/scripts/status.ts +159 -0
  135. package/scripts/sync-and-clean-tasks.js +258 -0
  136. package/scripts/sync-frames-from-railway.js +228 -0
  137. package/scripts/sync-linear-graphql.js +303 -0
  138. package/scripts/sync-linear-tasks.js +186 -0
  139. package/scripts/test-auto-triggers.sh +57 -0
  140. package/scripts/test-browser-mcp.js +74 -0
  141. package/scripts/test-chromadb-full.js +115 -0
  142. package/scripts/test-chromadb-hooks.sh +28 -0
  143. package/scripts/test-chromadb-sync.ts +245 -0
  144. package/scripts/test-cli-security.js +293 -0
  145. package/scripts/test-hooks-persistence.sh +220 -0
  146. package/scripts/test-installation-scenarios.sh +359 -0
  147. package/scripts/test-installation.sh +224 -0
  148. package/scripts/test-mcp.js +163 -0
  149. package/scripts/test-pre-publish-quick.sh +75 -0
  150. package/scripts/test-quality-gates.sh +263 -0
  151. package/scripts/test-railway-db.js +222 -0
  152. package/scripts/test-redis-storage.ts +490 -0
  153. package/scripts/test-rlm-basic.sh +122 -0
  154. package/scripts/test-rlm-comprehensive.sh +260 -0
  155. package/scripts/test-rlm-e2e.sh +268 -0
  156. package/scripts/test-rlm-simple.js +90 -0
  157. package/scripts/test-rlm.js +110 -0
  158. package/scripts/test-session-handoff.sh +165 -0
  159. package/scripts/test-shell-integration.sh +275 -0
  160. package/scripts/testing/ab-test-runner.ts +508 -0
  161. package/scripts/testing/collect-metrics.ts +457 -0
  162. package/scripts/testing/quick-effectiveness-demo.js +187 -0
  163. package/scripts/testing/real-performance-test.js +422 -0
  164. package/scripts/testing/run-effectiveness-tests.sh +176 -0
  165. package/scripts/testing/scripts/testing/ab-test-runner.js +363 -0
  166. package/scripts/testing/scripts/testing/collect-metrics.js +292 -0
  167. package/scripts/testing/simple-effectiveness-test.js +310 -0
  168. package/scripts/testing/src/core/context/context-bridge.js +253 -0
  169. package/scripts/testing/src/core/context/frame-manager.js +746 -0
  170. package/scripts/testing/src/core/context/shared-context-layer.js +437 -0
  171. package/scripts/testing/src/core/database/database-adapter.js +54 -0
  172. package/scripts/testing/src/core/errors/index.js +291 -0
  173. package/scripts/testing/src/core/errors/recovery.js +268 -0
  174. package/scripts/testing/src/core/monitoring/logger.js +145 -0
  175. package/scripts/testing/src/core/retrieval/context-retriever.js +516 -0
  176. package/scripts/testing/src/core/session/index.js +1 -0
  177. package/scripts/testing/src/core/session/session-manager.js +323 -0
  178. package/scripts/testing/src/core/trace/cli-trace-wrapper.js +140 -0
  179. package/scripts/testing/src/core/trace/db-trace-wrapper.js +251 -0
  180. package/scripts/testing/src/core/trace/debug-trace.js +398 -0
  181. package/scripts/testing/src/core/trace/index.js +120 -0
  182. package/scripts/testing/src/core/trace/linear-api-wrapper.js +204 -0
  183. package/scripts/update-linear-status.js +268 -0
  184. package/scripts/update-linear-tasks-fixed.js +284 -0
  185. package/templates/claude-hooks/hooks.json +5 -0
  186. package/templates/claude-hooks/on-clear.js +56 -0
  187. package/templates/claude-hooks/on-startup.js +56 -0
  188. package/templates/claude-hooks/tool-use-trace.js +67 -0
  189. package/dist/features/tui/components/analytics-panel.js +0 -157
  190. package/dist/features/tui/components/analytics-panel.js.map +0 -7
  191. package/dist/features/tui/components/frame-visualizer.js +0 -377
  192. package/dist/features/tui/components/frame-visualizer.js.map +0 -7
  193. package/dist/features/tui/components/pr-tracker.js +0 -135
  194. package/dist/features/tui/components/pr-tracker.js.map +0 -7
  195. package/dist/features/tui/components/session-monitor.js +0 -299
  196. package/dist/features/tui/components/session-monitor.js.map +0 -7
  197. package/dist/features/tui/components/subagent-fleet.js +0 -395
  198. package/dist/features/tui/components/subagent-fleet.js.map +0 -7
  199. package/dist/features/tui/components/task-board.js +0 -1139
  200. package/dist/features/tui/components/task-board.js.map +0 -7
  201. package/dist/features/tui/index.js +0 -408
  202. package/dist/features/tui/index.js.map +0 -7
  203. package/dist/features/tui/services/data-service.js +0 -641
  204. package/dist/features/tui/services/data-service.js.map +0 -7
  205. package/dist/features/tui/services/linear-task-reader.js +0 -102
  206. package/dist/features/tui/services/linear-task-reader.js.map +0 -7
  207. package/dist/features/tui/services/websocket-client.js +0 -162
  208. package/dist/features/tui/services/websocket-client.js.map +0 -7
  209. package/dist/features/tui/terminal-compat.js +0 -220
  210. package/dist/features/tui/terminal-compat.js.map +0 -7
  211. package/dist/features/tui/types.js +0 -1
  212. package/dist/features/tui/types.js.map +0 -7
@@ -0,0 +1,327 @@
1
+ #!/bin/bash
2
+
3
+ # Claude-specific Git Worktree Management for Multiple Instances
4
+ # Prevents conflicts when multiple Claude instances work on the same repository
5
+
6
+ set -e
7
+
8
+ # Configuration
9
+ WORKTREE_BASE_DIR="${WORKTREE_BASE_DIR:-../}"
10
+ CLAUDE_INSTANCE_ID="${CLAUDE_INSTANCE_ID:-$(uuidgen | cut -c1-8)}"
11
+ LOCK_DIR=".claude-worktree-locks"
12
+
13
+ # Colors for output
14
+ RED='\033[0;31m'
15
+ GREEN='\033[0;32m'
16
+ YELLOW='\033[1;33m'
17
+ NC='\033[0m' # No Color
18
+
19
+ # Initialize lock directory
20
+ init_locks() {
21
+ mkdir -p "$LOCK_DIR"
22
+ }
23
+
24
+ # Create a Claude-specific worktree with instance isolation
25
+ claude_worktree_create() {
26
+ local branch_base="$1"
27
+ local task_desc="${2:-work}"
28
+
29
+ if [[ -z "$branch_base" ]]; then
30
+ echo -e "${RED}Usage: claude_worktree_create <branch-name-base> [task-description]${NC}"
31
+ return 1
32
+ fi
33
+
34
+ # Generate unique branch name with timestamp and instance ID
35
+ local timestamp=$(date +%Y%m%d-%H%M%S)
36
+ local branch="claude-${branch_base}-${timestamp}-${CLAUDE_INSTANCE_ID}"
37
+ local repo_name="$(basename "$PWD")"
38
+ local worktree_path="${WORKTREE_BASE_DIR}${repo_name}--${branch}"
39
+
40
+ # Create lock file
41
+ init_locks
42
+ local lock_file="${LOCK_DIR}/${branch}.lock"
43
+
44
+ # Check if branch already exists
45
+ if git show-ref --verify --quiet "refs/heads/${branch}"; then
46
+ echo -e "${YELLOW}Branch ${branch} already exists, using existing branch${NC}"
47
+ git worktree add "$worktree_path" "$branch"
48
+ else
49
+ echo -e "${GREEN}Creating worktree: ${worktree_path}${NC}"
50
+ echo -e "${GREEN}Branch: ${branch}${NC}"
51
+ git worktree add -b "$branch" "$worktree_path"
52
+ fi
53
+
54
+ # Write lock information
55
+ cat > "$lock_file" <<EOF
56
+ {
57
+ "instance_id": "${CLAUDE_INSTANCE_ID}",
58
+ "branch": "${branch}",
59
+ "path": "${worktree_path}",
60
+ "created": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
61
+ "task": "${task_desc}",
62
+ "pid": "$$"
63
+ }
64
+ EOF
65
+
66
+ # Copy environment files if they exist
67
+ for file in .env .env.local .mise.toml .tool-versions; do
68
+ if [[ -f "$file" ]]; then
69
+ cp "$file" "$worktree_path/" 2>/dev/null || true
70
+ fi
71
+ done
72
+
73
+ # Trust the directory if mise is available
74
+ if command -v mise &> /dev/null; then
75
+ mise trust "$worktree_path" 2>/dev/null || true
76
+ fi
77
+
78
+ echo -e "${GREEN}Worktree created successfully!${NC}"
79
+ echo "Path: $worktree_path"
80
+ echo "Branch: $branch"
81
+ echo "Instance ID: $CLAUDE_INSTANCE_ID"
82
+ echo
83
+ echo "To switch to this worktree:"
84
+ echo " cd $worktree_path"
85
+ }
86
+
87
+ # List all Claude worktrees with status
88
+ claude_worktree_list() {
89
+ echo -e "${GREEN}=== Claude Worktrees ===${NC}"
90
+ echo
91
+
92
+ git worktree list --porcelain | while IFS= read -r line; do
93
+ if [[ "$line" == worktree* ]]; then
94
+ local path="${line#worktree }"
95
+ local dirname="$(basename "$path")"
96
+
97
+ # Check if this is a Claude worktree
98
+ if [[ "$dirname" == *"--claude-"* ]]; then
99
+ echo -e "${YELLOW}Worktree: ${dirname}${NC}"
100
+ echo " Path: $path"
101
+
102
+ # Read lock file if exists
103
+ local branch="${dirname#*--}"
104
+ local lock_file="${LOCK_DIR}/${branch}.lock"
105
+ if [[ -f "$lock_file" ]]; then
106
+ local instance_id=$(grep '"instance_id"' "$lock_file" | cut -d'"' -f4)
107
+ local task=$(grep '"task"' "$lock_file" | cut -d'"' -f4)
108
+ local created=$(grep '"created"' "$lock_file" | cut -d'"' -f4)
109
+ echo " Instance: $instance_id"
110
+ echo " Task: $task"
111
+ echo " Created: $created"
112
+ fi
113
+
114
+ # Check for uncommitted changes
115
+ if cd "$path" 2>/dev/null; then
116
+ if [[ -n $(git status --porcelain) ]]; then
117
+ echo -e " Status: ${RED}Has uncommitted changes${NC}"
118
+ else
119
+ echo -e " Status: ${GREEN}Clean${NC}"
120
+ fi
121
+ cd - > /dev/null
122
+ fi
123
+ echo
124
+ fi
125
+ fi
126
+ done
127
+ }
128
+
129
+ # Remove a Claude worktree safely
130
+ claude_worktree_remove() {
131
+ local branch="$1"
132
+
133
+ if [[ -z "$branch" ]]; then
134
+ # Try to detect from current directory
135
+ local current_dir="$(basename "$PWD")"
136
+ if [[ "$current_dir" == *"--claude-"* ]]; then
137
+ branch="${current_dir#*--}"
138
+ else
139
+ echo -e "${RED}Usage: claude_worktree_remove <branch-name>${NC}"
140
+ echo "Or run from within a Claude worktree directory"
141
+ return 1
142
+ fi
143
+ fi
144
+
145
+ # Find worktree path
146
+ local worktree_path
147
+ worktree_path=$(git worktree list --porcelain | grep -B1 "branch refs/heads/${branch}" | grep "^worktree" | cut -d' ' -f2)
148
+
149
+ if [[ -z "$worktree_path" ]]; then
150
+ echo -e "${RED}Worktree for branch ${branch} not found${NC}"
151
+ return 1
152
+ fi
153
+
154
+ echo -e "${YELLOW}Removing worktree: ${worktree_path}${NC}"
155
+ echo -e "${YELLOW}Branch: ${branch}${NC}"
156
+
157
+ # Check for uncommitted changes
158
+ if cd "$worktree_path" 2>/dev/null; then
159
+ if [[ -n $(git status --porcelain) ]]; then
160
+ echo -e "${RED}Warning: Worktree has uncommitted changes!${NC}"
161
+ read -p "Continue anyway? (y/N): " -n 1 -r
162
+ echo
163
+ if [[ ! $REPLY =~ ^[Yy]$ ]]; then
164
+ return 1
165
+ fi
166
+ fi
167
+ cd - > /dev/null
168
+ fi
169
+
170
+ # Remove worktree and branch
171
+ git worktree remove "$worktree_path" --force
172
+ git branch -D "$branch" 2>/dev/null || true
173
+
174
+ # Remove lock file
175
+ rm -f "${LOCK_DIR}/${branch}.lock"
176
+
177
+ echo -e "${GREEN}Worktree removed successfully${NC}"
178
+ }
179
+
180
+ # Clean up old Claude worktrees
181
+ claude_worktree_cleanup() {
182
+ local days="${1:-7}"
183
+ echo -e "${YELLOW}Cleaning up Claude worktrees older than ${days} days...${NC}"
184
+
185
+ local count=0
186
+ git worktree list --porcelain | while IFS= read -r line; do
187
+ if [[ "$line" == worktree* ]]; then
188
+ local path="${line#worktree }"
189
+ local dirname="$(basename "$path")"
190
+
191
+ # Check if this is a Claude worktree
192
+ if [[ "$dirname" == *"--claude-"* ]]; then
193
+ local branch="${dirname#*--}"
194
+ local lock_file="${LOCK_DIR}/${branch}.lock"
195
+
196
+ if [[ -f "$lock_file" ]]; then
197
+ local created=$(grep '"created"' "$lock_file" | cut -d'"' -f4)
198
+ local created_timestamp=$(date -d "$created" +%s 2>/dev/null || date -j -f "%Y-%m-%dT%H:%M:%SZ" "$created" +%s 2>/dev/null)
199
+ local current_timestamp=$(date +%s)
200
+ local age_days=$(( (current_timestamp - created_timestamp) / 86400 ))
201
+
202
+ if [[ $age_days -gt $days ]]; then
203
+ echo " Removing old worktree: $dirname (${age_days} days old)"
204
+ claude_worktree_remove "$branch"
205
+ ((count++))
206
+ fi
207
+ fi
208
+ fi
209
+ fi
210
+ done
211
+
212
+ echo -e "${GREEN}Cleaned up ${count} old worktrees${NC}"
213
+ }
214
+
215
+ # Sync worktree with main branch
216
+ claude_worktree_sync() {
217
+ local current_branch=$(git rev-parse --abbrev-ref HEAD)
218
+
219
+ if [[ ! "$current_branch" == claude-* ]]; then
220
+ echo -e "${RED}Not in a Claude worktree branch${NC}"
221
+ return 1
222
+ fi
223
+
224
+ echo -e "${YELLOW}Syncing with main branch...${NC}"
225
+
226
+ # Stash any local changes
227
+ local stash_result=$(git stash)
228
+
229
+ # Fetch and merge/rebase with main
230
+ git fetch origin
231
+ git rebase origin/main || git rebase origin/master || {
232
+ echo -e "${RED}Failed to rebase. You may need to resolve conflicts.${NC}"
233
+ if [[ "$stash_result" != "No local changes to save" ]]; then
234
+ git stash pop
235
+ fi
236
+ return 1
237
+ }
238
+
239
+ # Pop stash if we stashed anything
240
+ if [[ "$stash_result" != "No local changes to save" ]]; then
241
+ git stash pop
242
+ fi
243
+
244
+ echo -e "${GREEN}Sync completed successfully${NC}"
245
+ }
246
+
247
+ # Helper function to switch to a Claude worktree
248
+ claude_worktree_switch() {
249
+ local pattern="$1"
250
+
251
+ if [[ -z "$pattern" ]]; then
252
+ echo -e "${RED}Usage: claude_worktree_switch <pattern>${NC}"
253
+ echo "Pattern can be part of branch name or instance ID"
254
+ return 1
255
+ fi
256
+
257
+ local matches=()
258
+ git worktree list --porcelain | while IFS= read -r line; do
259
+ if [[ "$line" == worktree* ]]; then
260
+ local path="${line#worktree }"
261
+ local dirname="$(basename "$path")"
262
+
263
+ if [[ "$dirname" == *"claude"*"$pattern"* ]]; then
264
+ matches+=("$path")
265
+ echo "Found: $path"
266
+ fi
267
+ fi
268
+ done
269
+
270
+ if [[ ${#matches[@]} -eq 1 ]]; then
271
+ cd "${matches[0]}"
272
+ echo -e "${GREEN}Switched to: ${matches[0]}${NC}"
273
+ elif [[ ${#matches[@]} -gt 1 ]]; then
274
+ echo -e "${YELLOW}Multiple matches found. Please be more specific.${NC}"
275
+ else
276
+ echo -e "${RED}No matching worktree found${NC}"
277
+ fi
278
+ }
279
+
280
+ # Main function for CLI usage
281
+ main() {
282
+ local command="$1"
283
+ shift
284
+
285
+ case "$command" in
286
+ create|add)
287
+ claude_worktree_create "$@"
288
+ ;;
289
+ list|ls)
290
+ claude_worktree_list
291
+ ;;
292
+ remove|rm)
293
+ claude_worktree_remove "$@"
294
+ ;;
295
+ cleanup|clean)
296
+ claude_worktree_cleanup "$@"
297
+ ;;
298
+ sync)
299
+ claude_worktree_sync
300
+ ;;
301
+ switch|cd)
302
+ claude_worktree_switch "$@"
303
+ ;;
304
+ *)
305
+ echo "Claude Worktree Manager for Multiple Instances"
306
+ echo
307
+ echo "Usage: $0 <command> [options]"
308
+ echo
309
+ echo "Commands:"
310
+ echo " create <branch-base> [task] - Create a new Claude worktree"
311
+ echo " list - List all Claude worktrees"
312
+ echo " remove [branch] - Remove a Claude worktree"
313
+ echo " cleanup [days] - Remove old worktrees (default: 7 days)"
314
+ echo " sync - Sync current worktree with main branch"
315
+ echo " switch <pattern> - Switch to a Claude worktree"
316
+ echo
317
+ echo "Environment Variables:"
318
+ echo " CLAUDE_INSTANCE_ID - Unique ID for this Claude instance"
319
+ echo " WORKTREE_BASE_DIR - Base directory for worktrees (default: ../)"
320
+ ;;
321
+ esac
322
+ }
323
+
324
+ # Run main function if script is executed directly
325
+ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
326
+ main "$@"
327
+ fi
@@ -0,0 +1,273 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from 'fs';
4
+ import path from 'path';
5
+ import { fileURLToPath } from 'url';
6
+ import dotenv from 'dotenv';
7
+
8
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
9
+
10
+ // Load environment variables from .env file first
11
+ dotenv.config({
12
+ path: path.join(__dirname, '..', '.env'),
13
+ override: true // Override to ensure we use the latest key
14
+ });
15
+
16
+ async function queryLinear(query, variables = {}) {
17
+ const response = await fetch('https://api.linear.app/graphql', {
18
+ method: 'POST',
19
+ headers: {
20
+ 'Content-Type': 'application/json',
21
+ 'Authorization': process.env.LINEAR_API_KEY
22
+ },
23
+ body: JSON.stringify({ query, variables })
24
+ });
25
+
26
+ const data = await response.json();
27
+ if (data.errors) {
28
+ throw new Error(data.errors[0].message);
29
+ }
30
+ return data.data;
31
+ }
32
+
33
+ async function cleanLinearBacklog() {
34
+ const apiKey = process.env.LINEAR_API_KEY;
35
+
36
+ if (!apiKey) {
37
+ console.error('โŒ LINEAR_API_KEY not found in environment');
38
+ process.exit(1);
39
+ }
40
+
41
+ console.log('๐Ÿ” Analyzing Linear backlog...\n');
42
+
43
+ try {
44
+ // Fetch ALL issues (including completed/cancelled)
45
+ const issuesData = await queryLinear(`
46
+ query {
47
+ issues(first: 250, orderBy: updatedAt) {
48
+ nodes {
49
+ id
50
+ identifier
51
+ title
52
+ description
53
+ state {
54
+ name
55
+ type
56
+ }
57
+ priority
58
+ priorityLabel
59
+ team {
60
+ key
61
+ name
62
+ }
63
+ assignee {
64
+ name
65
+ email
66
+ }
67
+ createdAt
68
+ updatedAt
69
+ completedAt
70
+ canceledAt
71
+ url
72
+ }
73
+ }
74
+ }
75
+ `);
76
+
77
+ const allIssues = issuesData.issues.nodes;
78
+ console.log(`๐Ÿ“Š Total issues found: ${allIssues.length}`);
79
+
80
+ // Categorize issues
81
+ const byStatus = {
82
+ completed: [],
83
+ cancelled: [],
84
+ backlog: [],
85
+ unstarted: [],
86
+ started: [],
87
+ inProgress: []
88
+ };
89
+
90
+ const duplicates = new Map(); // Track potential duplicates
91
+ const engineeringMeetings = [];
92
+ const testTasks = [];
93
+
94
+ for (const issue of allIssues) {
95
+ // Categorize by status
96
+ if (issue.state.type === 'completed') {
97
+ byStatus.completed.push(issue);
98
+ } else if (issue.state.type === 'canceled' || issue.state.type === 'cancelled') {
99
+ byStatus.cancelled.push(issue);
100
+ } else if (issue.state.type === 'backlog') {
101
+ byStatus.backlog.push(issue);
102
+ } else if (issue.state.type === 'unstarted') {
103
+ byStatus.unstarted.push(issue);
104
+ } else if (issue.state.type === 'started') {
105
+ byStatus.started.push(issue);
106
+ }
107
+
108
+ // Check for duplicates (normalize title for comparison)
109
+ const normalizedTitle = issue.title
110
+ .replace(/^\[[^\]]+\]\s*/, '') // Remove [ENG-XXX] prefix
111
+ .replace(/^\[.*?\]\s*/, '') // Remove priority markers
112
+ .trim()
113
+ .toLowerCase();
114
+
115
+ if (!duplicates.has(normalizedTitle)) {
116
+ duplicates.set(normalizedTitle, []);
117
+ }
118
+ duplicates.get(normalizedTitle).push(issue);
119
+
120
+ // Identify meeting tasks
121
+ if (issue.title.includes('Engineering x') || issue.title.includes('Meeting')) {
122
+ engineeringMeetings.push(issue);
123
+ }
124
+
125
+ // Identify test tasks
126
+ if (issue.title.toLowerCase().includes('test') &&
127
+ (issue.title.includes('test-') || issue.title.includes('TEST]'))) {
128
+ testTasks.push(issue);
129
+ }
130
+ }
131
+
132
+ // Find actual duplicates (same title appearing multiple times)
133
+ const actualDuplicates = [];
134
+ for (const [title, issues] of duplicates.entries()) {
135
+ if (issues.length > 1) {
136
+ actualDuplicates.push({ title, issues });
137
+ }
138
+ }
139
+
140
+ // Report findings
141
+ console.log('\n๐Ÿ“ˆ Status Breakdown:');
142
+ console.log(` โœ… Completed: ${byStatus.completed.length}`);
143
+ console.log(` โŒ Cancelled: ${byStatus.cancelled.length}`);
144
+ console.log(` ๐Ÿ“‹ Backlog: ${byStatus.backlog.length}`);
145
+ console.log(` โณ Unstarted: ${byStatus.unstarted.length}`);
146
+ console.log(` ๐Ÿ”„ Started: ${byStatus.started.length}`);
147
+
148
+ console.log('\n๐Ÿ” Issues to Clean:');
149
+ console.log(` ๐Ÿ—“๏ธ Engineering Meetings: ${engineeringMeetings.length}`);
150
+ console.log(` ๐Ÿงช Test Tasks: ${testTasks.length}`);
151
+ console.log(` ๐Ÿ”„ Duplicate Titles: ${actualDuplicates.length}`);
152
+
153
+ // Show duplicates
154
+ if (actualDuplicates.length > 0) {
155
+ console.log('\n๐Ÿ“‘ Duplicate Issues:');
156
+ for (const dup of actualDuplicates.slice(0, 10)) {
157
+ console.log(`\n "${dup.title}":`);
158
+ for (const issue of dup.issues) {
159
+ const status = issue.state.type === 'completed' ? 'โœ…' :
160
+ issue.state.type === 'cancelled' ? 'โŒ' : 'โณ';
161
+ console.log(` ${status} ${issue.identifier}: ${issue.state.name}`);
162
+ }
163
+ }
164
+ if (actualDuplicates.length > 10) {
165
+ console.log(` ... and ${actualDuplicates.length - 10} more duplicate groups`);
166
+ }
167
+ }
168
+
169
+ // Recommend deletions
170
+ const toDelete = [];
171
+ const toArchive = [];
172
+
173
+ // Add completed issues older than 30 days
174
+ const thirtyDaysAgo = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000);
175
+ for (const issue of byStatus.completed) {
176
+ if (new Date(issue.completedAt) < thirtyDaysAgo) {
177
+ toArchive.push(issue);
178
+ }
179
+ }
180
+
181
+ // Add all cancelled issues
182
+ toDelete.push(...byStatus.cancelled);
183
+
184
+ // Add test tasks
185
+ toDelete.push(...testTasks.filter(t => t.state.type !== 'started'));
186
+
187
+ // For duplicates, keep the most recent one
188
+ for (const dup of actualDuplicates) {
189
+ const sorted = dup.issues.sort((a, b) =>
190
+ new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime()
191
+ );
192
+ // Mark all but the first (most recent) for deletion
193
+ for (let i = 1; i < sorted.length; i++) {
194
+ if (sorted[i].state.type !== 'completed' && sorted[i].state.type !== 'started') {
195
+ toDelete.push(sorted[i]);
196
+ }
197
+ }
198
+ }
199
+
200
+ // Remove duplicates from delete list
201
+ const uniqueToDelete = Array.from(new Set(toDelete.map(i => i.id)))
202
+ .map(id => toDelete.find(i => i.id === id));
203
+
204
+ console.log('\n๐Ÿ—‘๏ธ Recommended Actions:');
205
+ console.log(` ๐Ÿ“ Archive (completed >30 days): ${toArchive.length}`);
206
+ console.log(` ๐Ÿ—‘๏ธ Delete (cancelled/test/duplicates): ${uniqueToDelete.length}`);
207
+
208
+ if (toArchive.length > 0) {
209
+ console.log('\n๐Ÿ“ Issues to Archive:');
210
+ for (const issue of toArchive.slice(0, 5)) {
211
+ console.log(` - ${issue.identifier}: ${issue.title}`);
212
+ }
213
+ if (toArchive.length > 5) {
214
+ console.log(` ... and ${toArchive.length - 5} more`);
215
+ }
216
+ }
217
+
218
+ if (uniqueToDelete.length > 0) {
219
+ console.log('\n๐Ÿ—‘๏ธ Issues to Delete:');
220
+ const deleteByReason = {
221
+ cancelled: uniqueToDelete.filter(i => i.state.type === 'canceled' || i.state.type === 'cancelled'),
222
+ test: uniqueToDelete.filter(i => i.title.toLowerCase().includes('test')),
223
+ duplicate: uniqueToDelete.filter(i =>
224
+ !i.title.toLowerCase().includes('test') &&
225
+ i.state.type !== 'canceled' &&
226
+ i.state.type !== 'cancelled'
227
+ )
228
+ };
229
+
230
+ console.log(` Cancelled: ${deleteByReason.cancelled.length}`);
231
+ console.log(` Test: ${deleteByReason.test.length}`);
232
+ console.log(` Duplicate: ${deleteByReason.duplicate.length}`);
233
+
234
+ // Save deletion list
235
+ const deletionList = uniqueToDelete.map(i => ({
236
+ id: i.id,
237
+ identifier: i.identifier,
238
+ title: i.title,
239
+ state: i.state.type,
240
+ reason: i.state.type === 'canceled' || i.state.type === 'cancelled' ? 'cancelled' :
241
+ i.title.toLowerCase().includes('test') ? 'test' : 'duplicate'
242
+ }));
243
+
244
+ const filename = `linear-cleanup-${new Date().toISOString().split('T')[0]}.json`;
245
+ fs.writeFileSync(
246
+ path.join(__dirname, '..', filename),
247
+ JSON.stringify(deletionList, null, 2)
248
+ );
249
+ console.log(`\n๐Ÿ’พ Deletion list saved to: ${filename}`);
250
+ }
251
+
252
+ // Summary
253
+ console.log('\n๐Ÿ“Š Final Summary:');
254
+ const activeIssues = allIssues.filter(i =>
255
+ i.state.type !== 'completed' &&
256
+ i.state.type !== 'canceled' &&
257
+ i.state.type !== 'cancelled'
258
+ );
259
+ console.log(` Current backlog size: ${activeIssues.length}`);
260
+ console.log(` After cleanup: ${activeIssues.length - uniqueToDelete.length}`);
261
+ console.log(` Reduction: ${Math.round((uniqueToDelete.length / activeIssues.length) * 100)}%`);
262
+
263
+ console.log('\n๐Ÿ’ก Next Steps:');
264
+ console.log(' 1. Review the deletion list in the JSON file');
265
+ console.log(' 2. Use Linear\'s bulk operations to archive/delete');
266
+ console.log(' 3. Run the hourly sync daemon: ./scripts/start-linear-sync-daemon.sh start');
267
+
268
+ } catch (error) {
269
+ console.error('โŒ Error analyzing Linear backlog:', error.message);
270
+ }
271
+ }
272
+
273
+ cleanLinearBacklog();
@@ -0,0 +1,57 @@
1
+ #!/bin/bash
2
+
3
+ # StackMemory Session Cleanup Script
4
+
5
+ echo "๐Ÿงน StackMemory Session Cleanup"
6
+ echo "=============================="
7
+
8
+ SESSIONS_DIR="$HOME/.stackmemory/sessions"
9
+ DAYS_TO_KEEP=7
10
+
11
+ # Count current sessions
12
+ TOTAL_SESSIONS=$(find "$SESSIONS_DIR" -type f -name "*.json" | wc -l | tr -d ' ')
13
+ echo "๐Ÿ“Š Total sessions: $TOTAL_SESSIONS"
14
+
15
+ # Count old sessions
16
+ OLD_SESSIONS=$(find "$SESSIONS_DIR" -type f -name "*.json" -mtime +$DAYS_TO_KEEP | wc -l | tr -d ' ')
17
+ echo "๐Ÿ—“๏ธ Sessions older than $DAYS_TO_KEEP days: $OLD_SESSIONS"
18
+
19
+ if [ "$OLD_SESSIONS" -eq 0 ]; then
20
+ echo "โœจ No old sessions to clean up!"
21
+ exit 0
22
+ fi
23
+
24
+ # Calculate space used
25
+ SPACE_BEFORE=$(du -sh "$SESSIONS_DIR" | cut -f1)
26
+ echo "๐Ÿ’พ Current space used: $SPACE_BEFORE"
27
+
28
+ # Ask for confirmation
29
+ read -p "โš ๏ธ Remove $OLD_SESSIONS sessions older than $DAYS_TO_KEEP days? (y/n) " -n 1 -r
30
+ echo
31
+ if [[ $REPLY =~ ^[Yy]$ ]]; then
32
+ # Create backup directory
33
+ BACKUP_DIR="$HOME/.stackmemory/session-backups/$(date +%Y%m%d_%H%M%S)"
34
+ mkdir -p "$BACKUP_DIR"
35
+
36
+ echo "๐Ÿ“ฆ Creating backup in $BACKUP_DIR..."
37
+
38
+ # Move old sessions to backup (sample a few for backup)
39
+ find "$SESSIONS_DIR" -type f -name "*.json" -mtime +$DAYS_TO_KEEP -print0 | \
40
+ head -z -n 100 | \
41
+ xargs -0 -I {} cp {} "$BACKUP_DIR/" 2>/dev/null
42
+
43
+ # Remove old sessions
44
+ find "$SESSIONS_DIR" -type f -name "*.json" -mtime +$DAYS_TO_KEEP -delete
45
+
46
+ # Count remaining sessions
47
+ REMAINING=$(find "$SESSIONS_DIR" -type f -name "*.json" | wc -l | tr -d ' ')
48
+ SPACE_AFTER=$(du -sh "$SESSIONS_DIR" | cut -f1)
49
+
50
+ echo "โœ… Cleanup complete!"
51
+ echo "๐Ÿ“Š Sessions removed: $((TOTAL_SESSIONS - REMAINING))"
52
+ echo "๐Ÿ“Š Sessions remaining: $REMAINING"
53
+ echo "๐Ÿ’พ Space after cleanup: $SPACE_AFTER"
54
+ echo "๐Ÿ“ฆ Sample backup saved to: $BACKUP_DIR"
55
+ else
56
+ echo "โŒ Cleanup cancelled"
57
+ fi