@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,288 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Security Analysis Script for StackMemory CLI/API
5
+ * Analyzes code for input validation and security vulnerabilities
6
+ */
7
+
8
+ import { readFileSync, readdirSync, statSync } from 'fs';
9
+ import { join, extname } from 'path';
10
+
11
+ const SECURITY_ISSUES = {
12
+ CRITICAL: [],
13
+ HIGH: [],
14
+ MEDIUM: [],
15
+ LOW: []
16
+ };
17
+
18
+ // Color helpers
19
+ const red = (str) => `\x1b[31m${str}\x1b[0m`;
20
+ const yellow = (str) => `\x1b[33m${str}\x1b[0m`;
21
+ const green = (str) => `\x1b[32m${str}\x1b[0m`;
22
+ const blue = (str) => `\x1b[34m${str}\x1b[0m`;
23
+
24
+ // Security patterns to check
25
+ const SECURITY_PATTERNS = {
26
+ SQL_INJECTION: {
27
+ patterns: [
28
+ /\.prepare\s*\(\s*[`"'].*\$\{.*\}.*[`"']\s*\)/g,
29
+ /\.prepare\s*\(\s*[`"'].*\+.*[`"']\s*\)/g,
30
+ /\.exec\s*\(\s*[^`"'].*\)/g,
31
+ /WHERE.*LIKE\s*\?/gi
32
+ ],
33
+ severity: 'HIGH',
34
+ description: 'Potential SQL injection vulnerability'
35
+ },
36
+ COMMAND_INJECTION: {
37
+ patterns: [
38
+ /exec\s*\(.*\$\{.*\}/g,
39
+ /execSync\s*\(.*\$\{.*\}/g,
40
+ /spawn\s*\(.*,\s*\[.*\$\{.*\}/g,
41
+ /child_process.*exec/g
42
+ ],
43
+ severity: 'CRITICAL',
44
+ description: 'Potential command injection vulnerability'
45
+ },
46
+ PATH_TRAVERSAL: {
47
+ patterns: [
48
+ /\.\.\//g,
49
+ /join\s*\(.*process\.cwd\(\).*,.*[^'"]\)/g,
50
+ /readFileSync\s*\([^'"].*\)/g
51
+ ],
52
+ severity: 'HIGH',
53
+ description: 'Potential path traversal vulnerability'
54
+ },
55
+ NO_INPUT_VALIDATION: {
56
+ patterns: [
57
+ /parseInt\s*\(.*\)\s*(?!.*isNaN)/g,
58
+ /JSON\.parse\s*\(.*\)\s*(?!.*try)/g,
59
+ /\.action\s*\(\s*async.*\{[\s\S]*?(?!validate|check|verify)[\s\S]*?\}\)/g
60
+ ],
61
+ severity: 'MEDIUM',
62
+ description: 'Missing input validation'
63
+ },
64
+ HARDCODED_SECRETS: {
65
+ patterns: [
66
+ /api[_-]?key\s*[:=]\s*["'][^"']+["']/gi,
67
+ /secret\s*[:=]\s*["'][^"']+["']/gi,
68
+ /token\s*[:=]\s*["'][^"']+["']/gi,
69
+ /password\s*[:=]\s*["'][^"']+["']/gi
70
+ ],
71
+ severity: 'CRITICAL',
72
+ description: 'Hardcoded secrets detected'
73
+ },
74
+ UNSAFE_REGEX: {
75
+ patterns: [
76
+ /new RegExp\s*\(/g,
77
+ /\/\.\*.*\.\*\//g
78
+ ],
79
+ severity: 'LOW',
80
+ description: 'Potentially unsafe regular expression'
81
+ }
82
+ };
83
+
84
+ // Validation patterns found
85
+ const VALIDATION_PATTERNS = {
86
+ INPUT_SANITIZATION: [
87
+ /\.replace\s*\(\/\[.*\]\/g/,
88
+ /\.trim\(\)/,
89
+ /\.slice\(0,\s*\d+\)/,
90
+ /validator\./
91
+ ],
92
+ ERROR_HANDLING: [
93
+ /try\s*\{[\s\S]*?\}\s*catch/,
94
+ /\.catch\s*\(/,
95
+ /if\s*\(!.*\)\s*\{[\s\S]*?throw/,
96
+ /if\s*\(!.*\)\s*\{[\s\S]*?process\.exit/
97
+ ],
98
+ TYPE_CHECKING: [
99
+ /typeof.*===\s*['"]string['"]/,
100
+ /instanceof/,
101
+ /isNaN\s*\(/,
102
+ /Number\.isInteger/
103
+ ],
104
+ BOUNDS_CHECKING: [
105
+ /if\s*\(.*[<>]=?\s*\d+/,
106
+ /Math\.(min|max)\s*\(/,
107
+ /\.slice\(0,\s*\d+\)/
108
+ ],
109
+ SQL_PARAMETERIZATION: [
110
+ /\.prepare\s*\([\s\S]*?\?\s*[\s\S]*?\)/,
111
+ /\.all\s*\(.*\)/,
112
+ /\.run\s*\(.*\)/
113
+ ]
114
+ };
115
+
116
+ // Analyze a file
117
+ function analyzeFile(filePath) {
118
+ const content = readFileSync(filePath, 'utf8');
119
+ const lines = content.split('\n');
120
+ const fileName = filePath.replace(process.cwd() + '/', '');
121
+ const issues = [];
122
+ const validations = [];
123
+
124
+ // Check for security patterns
125
+ for (const [name, config] of Object.entries(SECURITY_PATTERNS)) {
126
+ for (const pattern of config.patterns) {
127
+ const matches = content.matchAll(pattern);
128
+ for (const match of matches) {
129
+ const lineNum = content.substring(0, match.index).split('\n').length;
130
+ const line = lines[lineNum - 1]?.trim() || '';
131
+
132
+ // Skip if it's in a comment
133
+ if (line.startsWith('//') || line.startsWith('*')) continue;
134
+
135
+ issues.push({
136
+ file: fileName,
137
+ line: lineNum,
138
+ type: name,
139
+ severity: config.severity,
140
+ description: config.description,
141
+ code: line.substring(0, 80)
142
+ });
143
+ }
144
+ }
145
+ }
146
+
147
+ // Check for validation patterns
148
+ for (const [type, patterns] of Object.entries(VALIDATION_PATTERNS)) {
149
+ for (const pattern of patterns) {
150
+ if (pattern.test(content)) {
151
+ validations.push({
152
+ file: fileName,
153
+ type: type,
154
+ found: true
155
+ });
156
+ }
157
+ }
158
+ }
159
+
160
+ return { issues, validations };
161
+ }
162
+
163
+ // Recursively find TypeScript files
164
+ function findFiles(dir, files = []) {
165
+ const items = readdirSync(dir);
166
+
167
+ for (const item of items) {
168
+ const fullPath = join(dir, item);
169
+ const stat = statSync(fullPath);
170
+
171
+ if (stat.isDirectory()) {
172
+ if (!item.includes('node_modules') && !item.startsWith('.')) {
173
+ findFiles(fullPath, files);
174
+ }
175
+ } else if (extname(item) === '.ts' || extname(item) === '.js') {
176
+ files.push(fullPath);
177
+ }
178
+ }
179
+
180
+ return files;
181
+ }
182
+
183
+ // Main analysis
184
+ function main() {
185
+ console.log(blue('\nšŸ” StackMemory CLI/API Security Analysis\n'));
186
+
187
+ const srcDir = join(process.cwd(), 'src');
188
+ const cliFiles = findFiles(join(srcDir, 'cli'));
189
+ const apiFiles = findFiles(join(srcDir, 'features', 'analytics', 'api'));
190
+ const allFiles = [...cliFiles, ...apiFiles];
191
+
192
+ console.log(`Analyzing ${allFiles.length} files...\n`);
193
+
194
+ const allIssues = [];
195
+ const allValidations = new Map();
196
+
197
+ for (const file of allFiles) {
198
+ const { issues, validations } = analyzeFile(file);
199
+ allIssues.push(...issues);
200
+
201
+ for (const validation of validations) {
202
+ const key = `${validation.file}:${validation.type}`;
203
+ allValidations.set(key, validation);
204
+ }
205
+ }
206
+
207
+ // Group issues by severity
208
+ for (const issue of allIssues) {
209
+ SECURITY_ISSUES[issue.severity].push(issue);
210
+ }
211
+
212
+ // Report findings
213
+ console.log(red('🚨 CRITICAL Issues:'), SECURITY_ISSUES.CRITICAL.length);
214
+ for (const issue of SECURITY_ISSUES.CRITICAL) {
215
+ console.log(` ${issue.file}:${issue.line}`);
216
+ console.log(` ${issue.description}`);
217
+ console.log(` ${yellow(issue.code)}`);
218
+ }
219
+
220
+ console.log(yellow('\nāš ļø HIGH Priority Issues:'), SECURITY_ISSUES.HIGH.length);
221
+ for (const issue of SECURITY_ISSUES.HIGH.slice(0, 10)) {
222
+ console.log(` ${issue.file}:${issue.line}`);
223
+ console.log(` ${issue.description}`);
224
+ }
225
+
226
+ console.log(blue('\nšŸ”µ MEDIUM Priority Issues:'), SECURITY_ISSUES.MEDIUM.length);
227
+ console.log(green('🟢 LOW Priority Issues:'), SECURITY_ISSUES.LOW.length);
228
+
229
+ // Report validation mechanisms found
230
+ console.log(green('\nāœ… Validation Mechanisms Found:'));
231
+ const validationTypes = {};
232
+ for (const validation of allValidations.values()) {
233
+ validationTypes[validation.type] = (validationTypes[validation.type] || 0) + 1;
234
+ }
235
+
236
+ for (const [type, count] of Object.entries(validationTypes)) {
237
+ console.log(` ${type}: ${count} occurrences`);
238
+ }
239
+
240
+ // Specific CLI command analysis
241
+ console.log(blue('\nšŸ“‹ CLI Command Analysis:'));
242
+ const commands = [
243
+ 'init', 'status', 'linear', 'search', 'projects', 'config',
244
+ 'analytics', 'tasks', 'context', 'session'
245
+ ];
246
+
247
+ for (const cmd of commands) {
248
+ const cmdFile = allFiles.find(f => f.includes(`commands/${cmd}.ts`));
249
+ if (cmdFile) {
250
+ const content = readFileSync(cmdFile, 'utf8');
251
+ const hasValidation = /validate|check|verify|isValid|sanitize/i.test(content);
252
+ const hasErrorHandling = /try\s*\{|\.catch\(|error\s*:/i.test(content);
253
+ const usesParams = /\.prepare\s*\([\s\S]*?\?/.test(content);
254
+
255
+ console.log(` ${cmd}:`);
256
+ console.log(` Input validation: ${hasValidation ? green('āœ“') : red('āœ—')}`);
257
+ console.log(` Error handling: ${hasErrorHandling ? green('āœ“') : red('āœ—')}`);
258
+ console.log(` SQL parameterized: ${usesParams ? green('āœ“') : yellow('⚠')}`);
259
+ }
260
+ }
261
+
262
+ // Summary and recommendations
263
+ console.log(blue('\nšŸ“Š Summary:'));
264
+ const total = allIssues.length;
265
+ const critical = SECURITY_ISSUES.CRITICAL.length;
266
+ const high = SECURITY_ISSUES.HIGH.length;
267
+
268
+ if (critical > 0) {
269
+ console.log(red(` āŒ ${critical} CRITICAL issues require immediate attention`));
270
+ }
271
+ if (high > 0) {
272
+ console.log(yellow(` āš ļø ${high} HIGH priority issues should be fixed soon`));
273
+ }
274
+
275
+ console.log(blue('\nšŸ”§ Recommendations:'));
276
+ console.log(' 1. Add input validation for all CLI arguments');
277
+ console.log(' 2. Use parameterized queries for all SQL operations');
278
+ console.log(' 3. Sanitize file paths to prevent traversal attacks');
279
+ console.log(' 4. Implement rate limiting for API endpoints');
280
+ console.log(' 5. Add comprehensive error handling');
281
+ console.log(' 6. Use environment variables for all sensitive data');
282
+ console.log(' 7. Implement proper authentication checks');
283
+ console.log(' 8. Add input length limits to prevent DoS');
284
+
285
+ process.exit(critical > 0 ? 1 : 0);
286
+ }
287
+
288
+ main();
@@ -0,0 +1,163 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Add all StackMemory phase tasks to Linear via API
5
+ */
6
+
7
+ import 'dotenv/config';
8
+ import fs from 'fs';
9
+ import { LinearRestClient } from '../dist/integrations/linear/rest-client.js';
10
+
11
+ const API_KEY = process.env.LINEAR_API_KEY;
12
+ if (!API_KEY) {
13
+ console.error('āŒ LINEAR_API_KEY environment variable not set');
14
+ console.log('Please set LINEAR_API_KEY in your .env file or export it in your shell');
15
+ process.exit(1);
16
+ }
17
+ const DELAY_BETWEEN_TASKS = 5000; // 5 seconds to avoid rate limits
18
+
19
+ const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
20
+
21
+ async function addTasksToLinear() {
22
+ try {
23
+ console.log('šŸš€ Starting to add StackMemory phase tasks to Linear...');
24
+
25
+ // Read the generated tasks
26
+ const tasksFile = `stackmemory-phase-tasks-${new Date().toISOString().split('T')[0]}.json`;
27
+ if (!fs.existsSync(tasksFile)) {
28
+ throw new Error(`Tasks file not found: ${tasksFile}`);
29
+ }
30
+
31
+ const taskData = JSON.parse(fs.readFileSync(tasksFile, 'utf8'));
32
+ console.log(`šŸ“‹ Found ${taskData.totalTasks} tasks to create`);
33
+
34
+ const client = new LinearRestClient(API_KEY);
35
+
36
+ // Get team info
37
+ console.log('šŸ” Getting team information...');
38
+ const team = await client.getTeam();
39
+ console.log(`šŸŽÆ Target team: ${team.name} (${team.key})`);
40
+
41
+ let created = 0;
42
+ let failed = 0;
43
+ const results = [];
44
+
45
+ // Process each phase
46
+ for (const [phaseName, tasks] of Object.entries(taskData.phases)) {
47
+ console.log(`\nšŸ“¦ Processing ${phaseName} (${tasks.length} tasks)`);
48
+
49
+ for (let i = 0; i < tasks.length; i++) {
50
+ const task = tasks[i];
51
+ console.log(`\nā³ Creating task ${i + 1}/${tasks.length}: ${task.title}`);
52
+
53
+ try {
54
+ // Create task via GraphQL
55
+ const createQuery = `
56
+ mutation CreateIssue($input: IssueCreateInput!) {
57
+ issueCreate(input: $input) {
58
+ success
59
+ issue {
60
+ id
61
+ identifier
62
+ title
63
+ state { name }
64
+ priority
65
+ url
66
+ }
67
+ }
68
+ }
69
+ `;
70
+
71
+ const taskInput = {
72
+ title: task.title.replace('STA-XXX:', '').trim(), // Remove placeholder ID
73
+ description: task.description,
74
+ teamId: team.id,
75
+ priority: task.priority
76
+ };
77
+
78
+ const response = await client.makeRequest(createQuery, { input: taskInput });
79
+
80
+ if (response.data?.issueCreate?.success) {
81
+ const createdTask = response.data.issueCreate.issue;
82
+ console.log(`āœ… Created: ${createdTask.identifier} - ${createdTask.title}`);
83
+ console.log(` URL: ${createdTask.url}`);
84
+
85
+ results.push({
86
+ original: task.title,
87
+ created: createdTask.identifier,
88
+ url: createdTask.url,
89
+ success: true
90
+ });
91
+ created++;
92
+ } else {
93
+ const errors = response.data?.issueCreate?.errors || [{ message: 'Unknown error' }];
94
+ throw new Error(errors[0].message);
95
+ }
96
+
97
+ // Delay to avoid rate limits (except for last task)
98
+ if (i < tasks.length - 1) {
99
+ console.log(`ā³ Waiting ${DELAY_BETWEEN_TASKS / 1000}s to avoid rate limits...`);
100
+ await delay(DELAY_BETWEEN_TASKS);
101
+ }
102
+
103
+ } catch (error) {
104
+ console.log(`āŒ Failed: ${error.message}`);
105
+ results.push({
106
+ original: task.title,
107
+ error: error.message,
108
+ success: false
109
+ });
110
+ failed++;
111
+
112
+ // If rate limited, wait longer and continue
113
+ if (error.message.includes('usage limit') || error.message.includes('rate limit')) {
114
+ console.log('🚫 Hit rate limit. Waiting 60 seconds...');
115
+ await delay(60000);
116
+ }
117
+ }
118
+ }
119
+ }
120
+
121
+ // Summary
122
+ console.log('\nšŸ“Š Creation Summary:');
123
+ console.log(`āœ… Successfully created: ${created} tasks`);
124
+ console.log(`āŒ Failed: ${failed} tasks`);
125
+ console.log(`šŸ“ˆ Success rate: ${Math.round((created / (created + failed)) * 100)}%`);
126
+
127
+ // Save results
128
+ const resultsFile = `linear-task-creation-results-${new Date().toISOString().split('T')[0]}.json`;
129
+ fs.writeFileSync(resultsFile, JSON.stringify({
130
+ summary: { created, failed, total: created + failed },
131
+ results: results,
132
+ timestamp: new Date().toISOString()
133
+ }, null, 2));
134
+
135
+ console.log(`šŸ’¾ Results saved to: ${resultsFile}`);
136
+
137
+ if (created > 0) {
138
+ console.log('\nšŸŽ‰ Success! StackMemory phase tasks have been added to Linear.');
139
+ console.log('šŸ’” Next steps:');
140
+ console.log(' 1. Review and organize tasks in Linear workspace');
141
+ console.log(' 2. Start with high-priority Phase 2 tasks');
142
+ console.log(' 3. Assign tasks to team members');
143
+ console.log(' 4. Begin implementation of LLM-driven context retrieval');
144
+ }
145
+
146
+ if (failed > 0) {
147
+ console.log('\nāš ļø Some tasks failed to create. Check the results file for details.');
148
+ console.log(' You can retry failed tasks manually using:');
149
+ console.log(' node dist/cli/index.js linear:create --api-key <key> --title "..." --description "..."');
150
+ }
151
+
152
+ } catch (error) {
153
+ console.error('šŸ’„ Script failed:', error.message);
154
+ process.exit(1);
155
+ }
156
+ }
157
+
158
+ // Run if called directly
159
+ if (import.meta.url === `file://${process.argv[1]}`) {
160
+ addTasksToLinear().catch(console.error);
161
+ }
162
+
163
+ export { addTasksToLinear };
@@ -0,0 +1,214 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Analyze Linear workspace for duplicate tasks to identify what to delete
5
+ */
6
+
7
+ import 'dotenv/config';
8
+ import { LinearRestClient } from '../dist/integrations/linear/rest-client.js';
9
+ import fs from 'fs';
10
+
11
+ const API_KEY = process.env.LINEAR_API_KEY;
12
+ if (!API_KEY) {
13
+ console.error('āŒ LINEAR_API_KEY environment variable not set');
14
+ console.log('Please set LINEAR_API_KEY in your .env file or export it in your shell');
15
+ process.exit(1);
16
+ }
17
+
18
+ async function analyzeLinearDuplicates() {
19
+ try {
20
+ console.log('šŸ” Analyzing Linear workspace for duplicates and low-value tasks...\n');
21
+
22
+ const client = new LinearRestClient(API_KEY);
23
+
24
+ // Fetch all tasks
25
+ console.log('šŸ“„ Fetching all tasks from Linear...');
26
+ const allTasks = await client.getAllTasks(true);
27
+ console.log(`šŸ“Š Total tasks: ${allTasks.length}\n`);
28
+
29
+ // Analyze patterns
30
+ const duplicates = new Map(); // title pattern -> tasks
31
+ const meetingTasks = [];
32
+ const todoTasks = [];
33
+ const analyticsDashboardTasks = [];
34
+ const phaseImportTasks = [];
35
+ const completedTasks = [];
36
+ const canceledTasks = [];
37
+
38
+ // Group tasks by patterns
39
+ allTasks.forEach(task => {
40
+ const title = task.title;
41
+ const state = task.state.type;
42
+
43
+ // Check for completed/canceled
44
+ if (state === 'completed') {
45
+ completedTasks.push(task);
46
+ } else if (state === 'canceled') {
47
+ canceledTasks.push(task);
48
+ }
49
+
50
+ // Engineering meetings pattern
51
+ if (title.includes('Engineering x') && title.includes('Meeting')) {
52
+ meetingTasks.push(task);
53
+ }
54
+
55
+ // Documentation TODOs pattern
56
+ if (title.includes('Complete Documentation TODOs')) {
57
+ todoTasks.push(task);
58
+ }
59
+
60
+ // Task Analytics Dashboard pattern
61
+ if (title.includes('Task Analytics Dashboard')) {
62
+ analyticsDashboardTasks.push(task);
63
+ }
64
+
65
+ // Phase tasks with duplicate numbers
66
+ if (title.match(/\[STA-\d+\]/)) {
67
+ const match = title.match(/\[(STA-\d+)\]/);
68
+ if (match) {
69
+ const innerRef = match[1];
70
+ if (!phaseImportTasks[innerRef]) {
71
+ phaseImportTasks[innerRef] = [];
72
+ }
73
+ phaseImportTasks[innerRef].push(task);
74
+ }
75
+ }
76
+
77
+ // Find exact duplicates by title
78
+ const baseTitle = title.replace(/\[.*?\]/g, '').trim();
79
+ if (!duplicates.has(baseTitle)) {
80
+ duplicates.set(baseTitle, []);
81
+ }
82
+ duplicates.get(baseTitle).push(task);
83
+ });
84
+
85
+ // Filter to only show actual duplicates
86
+ const trueDuplicates = Array.from(duplicates.entries())
87
+ .filter(([_, tasks]) => tasks.length > 1)
88
+ .sort((a, b) => b[1].length - a[1].length);
89
+
90
+ // Generate report
91
+ console.log('šŸ“‹ DUPLICATE ANALYSIS REPORT');
92
+ console.log('=' .repeat(60));
93
+
94
+ console.log('\nšŸ”„ EXACT DUPLICATES (same title):');
95
+ console.log(`Found ${trueDuplicates.length} duplicate groups\n`);
96
+ trueDuplicates.slice(0, 10).forEach(([title, tasks]) => {
97
+ console.log(` "${title}" - ${tasks.length} copies`);
98
+ tasks.slice(0, 3).forEach(task => {
99
+ console.log(` • ${task.identifier}: ${task.state.name}`);
100
+ });
101
+ });
102
+
103
+ console.log('\nšŸ¢ ENGINEERING MEETINGS:');
104
+ console.log(`Found ${meetingTasks.length} meeting tasks`);
105
+ if (meetingTasks.length > 0) {
106
+ console.log('Examples:');
107
+ meetingTasks.slice(0, 5).forEach(task => {
108
+ console.log(` • ${task.identifier}: ${task.title}`);
109
+ });
110
+ }
111
+
112
+ console.log('\nšŸ“ DOCUMENTATION TODO DUPLICATES:');
113
+ console.log(`Found ${todoTasks.length} TODO tasks`);
114
+ if (todoTasks.length > 0) {
115
+ console.log('Examples:');
116
+ todoTasks.slice(0, 5).forEach(task => {
117
+ console.log(` • ${task.identifier}: ${task.title}`);
118
+ });
119
+ }
120
+
121
+ console.log('\nšŸ“Š TASK ANALYTICS DASHBOARD DUPLICATES:');
122
+ console.log(`Found ${analyticsDashboardTasks.length} analytics dashboard tasks`);
123
+ if (analyticsDashboardTasks.length > 0) {
124
+ console.log('Examples:');
125
+ analyticsDashboardTasks.slice(0, 5).forEach(task => {
126
+ console.log(` • ${task.identifier}: ${task.title}`);
127
+ });
128
+ }
129
+
130
+ console.log('\nāœ… COMPLETED TASKS (can be archived):');
131
+ console.log(`Found ${completedTasks.length} completed tasks`);
132
+
133
+ console.log('\nāŒ CANCELED TASKS (can be deleted):');
134
+ console.log(`Found ${canceledTasks.length} canceled tasks`);
135
+
136
+ // Calculate deletion recommendations
137
+ const toDelete = [
138
+ ...meetingTasks,
139
+ ...todoTasks.slice(1), // Keep one TODO task
140
+ ...analyticsDashboardTasks.slice(1), // Keep one dashboard task
141
+ ...canceledTasks,
142
+ ...completedTasks
143
+ ];
144
+
145
+ // Remove duplicates from deletion list
146
+ const uniqueToDelete = Array.from(new Set(toDelete.map(t => t.id)))
147
+ .map(id => toDelete.find(t => t.id === id));
148
+
149
+ console.log('\nšŸ—‘ļø DELETION RECOMMENDATIONS');
150
+ console.log('=' .repeat(60));
151
+ console.log(`Total tasks to delete: ${uniqueToDelete.length}`);
152
+ console.log(`Space to be freed: ${uniqueToDelete.length} issues`);
153
+ console.log(`Remaining after deletion: ${allTasks.length - uniqueToDelete.length} issues\n`);
154
+
155
+ // Group by category for deletion
156
+ const deleteCategories = {
157
+ 'Engineering Meetings': meetingTasks.map(t => t.identifier),
158
+ 'Duplicate TODOs': todoTasks.slice(1).map(t => t.identifier),
159
+ 'Duplicate Dashboards': analyticsDashboardTasks.slice(1).map(t => t.identifier),
160
+ 'Completed Tasks': completedTasks.map(t => t.identifier),
161
+ 'Canceled Tasks': canceledTasks.map(t => t.identifier)
162
+ };
163
+
164
+ console.log('šŸ“ Tasks to delete by category:\n');
165
+ Object.entries(deleteCategories).forEach(([category, tasks]) => {
166
+ if (tasks.length > 0) {
167
+ console.log(`${category} (${tasks.length} tasks):`);
168
+ console.log(` ${tasks.slice(0, 10).join(', ')}${tasks.length > 10 ? '...' : ''}\n`);
169
+ }
170
+ });
171
+
172
+ // Save deletion list to file
173
+ const deleteList = {
174
+ timestamp: new Date().toISOString(),
175
+ summary: {
176
+ total: allTasks.length,
177
+ toDelete: uniqueToDelete.length,
178
+ remaining: allTasks.length - uniqueToDelete.length
179
+ },
180
+ categories: deleteCategories,
181
+ tasks: uniqueToDelete.map(t => ({
182
+ id: t.id,
183
+ identifier: t.identifier,
184
+ title: t.title,
185
+ state: t.state.name
186
+ }))
187
+ };
188
+
189
+ const filename = `linear-deletion-list-${new Date().toISOString().split('T')[0]}.json`;
190
+ fs.writeFileSync(filename, JSON.stringify(deleteList, null, 2));
191
+ console.log(`šŸ’¾ Deletion list saved to: ${filename}\n`);
192
+
193
+ console.log('šŸŽÆ NEXT STEPS:');
194
+ console.log('1. Review the deletion list above');
195
+ console.log('2. Run the delete script to remove these tasks');
196
+ console.log('3. Then add the new phase tasks\n');
197
+
198
+ console.log('To delete these tasks, run:');
199
+ console.log(' node scripts/delete-linear-tasks.js\n');
200
+
201
+ return deleteList;
202
+
203
+ } catch (error) {
204
+ console.error('āŒ Analysis failed:', error.message);
205
+ process.exit(1);
206
+ }
207
+ }
208
+
209
+ // Run if called directly
210
+ if (import.meta.url === `file://${process.argv[1]}`) {
211
+ analyzeLinearDuplicates().catch(console.error);
212
+ }
213
+
214
+ export { analyzeLinearDuplicates };