@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.
- package/dist/cli/commands/skills.js +15 -2
- package/dist/cli/commands/skills.js.map +2 -2
- package/dist/cli/index.js +113 -834
- package/dist/cli/index.js.map +3 -3
- package/dist/core/context/dual-stack-manager.js +1 -1
- package/dist/core/context/dual-stack-manager.js.map +1 -1
- package/dist/core/context/frame-manager.js +3 -0
- package/dist/core/context/frame-manager.js.map +2 -2
- package/dist/integrations/claude-code/subagent-client.js +106 -3
- package/dist/integrations/claude-code/subagent-client.js.map +2 -2
- package/dist/servers/railway/config.js +51 -0
- package/dist/servers/railway/config.js.map +7 -0
- package/dist/servers/railway/index-enhanced.js +156 -0
- package/dist/servers/railway/index-enhanced.js.map +7 -0
- package/dist/servers/railway/minimal.js +48 -3
- package/dist/servers/railway/minimal.js.map +2 -2
- package/dist/servers/railway/storage-test.js +455 -0
- package/dist/servers/railway/storage-test.js.map +7 -0
- package/dist/skills/claude-skills.js +13 -12
- package/dist/skills/claude-skills.js.map +2 -2
- package/dist/skills/recursive-agent-orchestrator.js +27 -18
- package/dist/skills/recursive-agent-orchestrator.js.map +2 -2
- package/dist/skills/unified-rlm-orchestrator.js.map +2 -2
- package/package.json +6 -18
- package/scripts/README-TESTING.md +186 -0
- package/scripts/analyze-cli-security.js +288 -0
- package/scripts/archive/add-phase-tasks-to-linear.js +163 -0
- package/scripts/archive/analyze-linear-duplicates.js +214 -0
- package/scripts/archive/analyze-remaining-duplicates.js +230 -0
- package/scripts/archive/analyze-sta-duplicates.js +292 -0
- package/scripts/archive/analyze-sta-graphql.js +399 -0
- package/scripts/archive/cancel-duplicate-tasks.ts +246 -0
- package/scripts/archive/check-all-duplicates.ts +419 -0
- package/scripts/archive/clean-duplicate-tasks.js +114 -0
- package/scripts/archive/cleanup-duplicate-tasks.ts +286 -0
- package/scripts/archive/create-phase-tasks.js +387 -0
- package/scripts/archive/delete-linear-duplicates.js +182 -0
- package/scripts/archive/delete-remaining-duplicates.js +158 -0
- package/scripts/archive/delete-sta-duplicates.js +201 -0
- package/scripts/archive/delete-sta-oauth.js +201 -0
- package/scripts/archive/export-sta-tasks.js +62 -0
- package/scripts/archive/install-auto-sync.js +266 -0
- package/scripts/archive/install-chromadb-hooks.sh +133 -0
- package/scripts/archive/install-enhanced-clear-hooks.sh +431 -0
- package/scripts/archive/install-post-task-hooks.sh +289 -0
- package/scripts/archive/install-stackmemory-hooks.sh +420 -0
- package/scripts/archive/merge-linear-duplicates-safe.ts +362 -0
- package/scripts/archive/merge-linear-duplicates.ts +180 -0
- package/scripts/archive/remove-sta-tasks.js +70 -0
- package/scripts/archive/setup-background-sync.sh +168 -0
- package/scripts/archive/setup-claude-auto-triggers.sh +181 -0
- package/scripts/archive/setup-claude-autostart.sh +305 -0
- package/scripts/archive/setup-git-hooks.sh +25 -0
- package/scripts/archive/setup-linear-oauth.sh +46 -0
- package/scripts/archive/setup-mcp.sh +113 -0
- package/scripts/archive/setup-railway-deployment.sh +81 -0
- package/scripts/auto-handoff.sh +262 -0
- package/scripts/background-sync-manager.js +416 -0
- package/scripts/benchmark-performance.ts +57 -0
- package/scripts/check-redis.ts +48 -0
- package/scripts/chromadb-auto-loader.sh +128 -0
- package/scripts/chromadb-context-loader.js +479 -0
- package/scripts/claude-chromadb-hook.js +460 -0
- package/scripts/claude-code-wrapper.sh +66 -0
- package/scripts/claude-linear-skill.js +455 -0
- package/scripts/claude-pre-commit.sh +302 -0
- package/scripts/claude-sm-autostart.js +532 -0
- package/scripts/claude-sm-setup.sh +367 -0
- package/scripts/claude-with-chromadb.sh +69 -0
- package/scripts/claude-worktree-manager.sh +323 -0
- package/scripts/claude-worktree-monitor.sh +371 -0
- package/scripts/claude-worktree-setup.sh +327 -0
- package/scripts/clean-linear-backlog.js +273 -0
- package/scripts/cleanup-old-sessions.sh +57 -0
- package/scripts/codex-wrapper.sh +88 -0
- package/scripts/create-sandbox.sh +269 -0
- package/scripts/debug-linear-update.js +174 -0
- package/scripts/delete-linear-tasks.js +167 -0
- package/scripts/deploy.sh +89 -0
- package/scripts/deployment/railway.sh +352 -0
- package/scripts/deployment/test-deployment.js +194 -0
- package/scripts/detect-and-rehydrate.js +162 -0
- package/scripts/detect-and-rehydrate.mjs +165 -0
- package/scripts/development/create-demo-tasks.js +143 -0
- package/scripts/development/debug-frame-test.js +16 -0
- package/scripts/development/demo-auto-sync.js +128 -0
- package/scripts/development/fix-all-imports.js +213 -0
- package/scripts/development/fix-imports.js +229 -0
- package/scripts/development/fix-lint-loop.cjs +103 -0
- package/scripts/development/fix-project-id.ts +161 -0
- package/scripts/development/fix-strict-mode-issues.ts +291 -0
- package/scripts/development/reorganize-structure.sh +228 -0
- package/scripts/development/test-persistence-direct.js +148 -0
- package/scripts/development/test-persistence.js +114 -0
- package/scripts/development/test-tasks.js +93 -0
- package/scripts/development/update-imports.js +212 -0
- package/scripts/fetch-linear-status.js +125 -0
- package/scripts/git-hooks/README.md +310 -0
- package/scripts/git-hooks/branch-context-manager.sh +342 -0
- package/scripts/git-hooks/post-checkout-stackmemory.sh +63 -0
- package/scripts/git-hooks/post-commit-stackmemory.sh +305 -0
- package/scripts/git-hooks/pre-commit-stackmemory.sh +275 -0
- package/scripts/hooks/cleanup-shell.sh +130 -0
- package/scripts/hooks/task-complete.sh +114 -0
- package/scripts/initialize.ts +129 -0
- package/scripts/install-claude-hooks-auto.js +104 -0
- package/scripts/install-claude-hooks.sh +133 -0
- package/scripts/install-global.sh +296 -0
- package/scripts/install.sh +235 -0
- package/scripts/linear-auto-sync.js +262 -0
- package/scripts/linear-auto-sync.sh +161 -0
- package/scripts/linear-sync-daemon.js +150 -0
- package/scripts/linear-task-review.js +237 -0
- package/scripts/list-linear-tasks.ts +178 -0
- package/scripts/mcp-proxy.js +66 -0
- package/scripts/opencode-wrapper.sh +85 -0
- package/scripts/publish-local.js +74 -0
- package/scripts/query-chromadb.ts +201 -0
- package/scripts/railway-env-setup.sh +39 -0
- package/scripts/reconcile-local-tasks.js +170 -0
- package/scripts/recreate-frames-db.js +89 -0
- package/scripts/setup/claude-integration.js +138 -0
- package/scripts/setup/configure-alias.js +125 -0
- package/scripts/setup/configure-codex-alias.js +161 -0
- package/scripts/setup/configure-opencode-alias.js +175 -0
- package/scripts/setup-claude-integration.js +204 -0
- package/scripts/setup-claude-integration.sh +183 -0
- package/scripts/setup.sh +31 -0
- package/scripts/show-linear-summary.ts +172 -0
- package/scripts/stackmemory-auto-handoff.sh +231 -0
- package/scripts/stackmemory-daemon.sh +40 -0
- package/scripts/start-linear-sync-daemon.sh +141 -0
- package/scripts/start-temporal-paradox.sh +214 -0
- package/scripts/status.ts +159 -0
- package/scripts/sync-and-clean-tasks.js +258 -0
- package/scripts/sync-frames-from-railway.js +228 -0
- package/scripts/sync-linear-graphql.js +303 -0
- package/scripts/sync-linear-tasks.js +186 -0
- package/scripts/test-auto-triggers.sh +57 -0
- package/scripts/test-browser-mcp.js +74 -0
- package/scripts/test-chromadb-full.js +115 -0
- package/scripts/test-chromadb-hooks.sh +28 -0
- package/scripts/test-chromadb-sync.ts +245 -0
- package/scripts/test-cli-security.js +293 -0
- package/scripts/test-hooks-persistence.sh +220 -0
- package/scripts/test-installation-scenarios.sh +359 -0
- package/scripts/test-installation.sh +224 -0
- package/scripts/test-mcp.js +163 -0
- package/scripts/test-pre-publish-quick.sh +75 -0
- package/scripts/test-quality-gates.sh +263 -0
- package/scripts/test-railway-db.js +222 -0
- package/scripts/test-redis-storage.ts +490 -0
- package/scripts/test-rlm-basic.sh +122 -0
- package/scripts/test-rlm-comprehensive.sh +260 -0
- package/scripts/test-rlm-e2e.sh +268 -0
- package/scripts/test-rlm-simple.js +90 -0
- package/scripts/test-rlm.js +110 -0
- package/scripts/test-session-handoff.sh +165 -0
- package/scripts/test-shell-integration.sh +275 -0
- package/scripts/testing/ab-test-runner.ts +508 -0
- package/scripts/testing/collect-metrics.ts +457 -0
- package/scripts/testing/quick-effectiveness-demo.js +187 -0
- package/scripts/testing/real-performance-test.js +422 -0
- package/scripts/testing/run-effectiveness-tests.sh +176 -0
- package/scripts/testing/scripts/testing/ab-test-runner.js +363 -0
- package/scripts/testing/scripts/testing/collect-metrics.js +292 -0
- package/scripts/testing/simple-effectiveness-test.js +310 -0
- package/scripts/testing/src/core/context/context-bridge.js +253 -0
- package/scripts/testing/src/core/context/frame-manager.js +746 -0
- package/scripts/testing/src/core/context/shared-context-layer.js +437 -0
- package/scripts/testing/src/core/database/database-adapter.js +54 -0
- package/scripts/testing/src/core/errors/index.js +291 -0
- package/scripts/testing/src/core/errors/recovery.js +268 -0
- package/scripts/testing/src/core/monitoring/logger.js +145 -0
- package/scripts/testing/src/core/retrieval/context-retriever.js +516 -0
- package/scripts/testing/src/core/session/index.js +1 -0
- package/scripts/testing/src/core/session/session-manager.js +323 -0
- package/scripts/testing/src/core/trace/cli-trace-wrapper.js +140 -0
- package/scripts/testing/src/core/trace/db-trace-wrapper.js +251 -0
- package/scripts/testing/src/core/trace/debug-trace.js +398 -0
- package/scripts/testing/src/core/trace/index.js +120 -0
- package/scripts/testing/src/core/trace/linear-api-wrapper.js +204 -0
- package/scripts/update-linear-status.js +268 -0
- package/scripts/update-linear-tasks-fixed.js +284 -0
- package/templates/claude-hooks/hooks.json +5 -0
- package/templates/claude-hooks/on-clear.js +56 -0
- package/templates/claude-hooks/on-startup.js +56 -0
- package/templates/claude-hooks/tool-use-trace.js +67 -0
- package/dist/features/tui/components/analytics-panel.js +0 -157
- package/dist/features/tui/components/analytics-panel.js.map +0 -7
- package/dist/features/tui/components/frame-visualizer.js +0 -377
- package/dist/features/tui/components/frame-visualizer.js.map +0 -7
- package/dist/features/tui/components/pr-tracker.js +0 -135
- package/dist/features/tui/components/pr-tracker.js.map +0 -7
- package/dist/features/tui/components/session-monitor.js +0 -299
- package/dist/features/tui/components/session-monitor.js.map +0 -7
- package/dist/features/tui/components/subagent-fleet.js +0 -395
- package/dist/features/tui/components/subagent-fleet.js.map +0 -7
- package/dist/features/tui/components/task-board.js +0 -1139
- package/dist/features/tui/components/task-board.js.map +0 -7
- package/dist/features/tui/index.js +0 -408
- package/dist/features/tui/index.js.map +0 -7
- package/dist/features/tui/services/data-service.js +0 -641
- package/dist/features/tui/services/data-service.js.map +0 -7
- package/dist/features/tui/services/linear-task-reader.js +0 -102
- package/dist/features/tui/services/linear-task-reader.js.map +0 -7
- package/dist/features/tui/services/websocket-client.js +0 -162
- package/dist/features/tui/services/websocket-client.js.map +0 -7
- package/dist/features/tui/terminal-compat.js +0 -220
- package/dist/features/tui/terminal-compat.js.map +0 -7
- package/dist/features/tui/types.js +0 -1
- package/dist/features/tui/types.js.map +0 -7
|
@@ -0,0 +1,303 @@
|
|
|
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
|
|
11
|
+
dotenv.config({
|
|
12
|
+
path: path.join(__dirname, '..', '.env'),
|
|
13
|
+
debug: false,
|
|
14
|
+
override: true,
|
|
15
|
+
silent: true
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
// Debug: Check if key is loaded
|
|
19
|
+
console.log(`API Key loaded: ${process.env.LINEAR_API_KEY ? 'Yes' : 'No'} (length: ${process.env.LINEAR_API_KEY?.length || 0})`);
|
|
20
|
+
|
|
21
|
+
async function queryLinear(query, variables = {}) {
|
|
22
|
+
const response = await fetch('https://api.linear.app/graphql', {
|
|
23
|
+
method: 'POST',
|
|
24
|
+
headers: {
|
|
25
|
+
'Content-Type': 'application/json',
|
|
26
|
+
'Authorization': process.env.LINEAR_API_KEY
|
|
27
|
+
},
|
|
28
|
+
body: JSON.stringify({ query, variables })
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const data = await response.json();
|
|
32
|
+
if (data.errors) {
|
|
33
|
+
throw new Error(data.errors[0].message);
|
|
34
|
+
}
|
|
35
|
+
return data.data;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async function syncLinearTasks() {
|
|
39
|
+
const apiKey = process.env.LINEAR_API_KEY;
|
|
40
|
+
|
|
41
|
+
if (!apiKey) {
|
|
42
|
+
console.error('โ LINEAR_API_KEY not found in environment');
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Check for --mirror flag to do a complete replacement
|
|
47
|
+
const isMirrorMode = process.argv.includes('--mirror');
|
|
48
|
+
|
|
49
|
+
if (isMirrorMode) {
|
|
50
|
+
console.log('๐ Running in MIRROR mode - will replace all local tasks with Linear tasks...');
|
|
51
|
+
} else {
|
|
52
|
+
console.log('๐ Connecting to Linear API...');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
try {
|
|
56
|
+
// Test connection
|
|
57
|
+
const viewer = await queryLinear('{ viewer { id email name } }');
|
|
58
|
+
console.log(`โ
Connected as: ${viewer.viewer.name || viewer.viewer.email}`);
|
|
59
|
+
|
|
60
|
+
// Get teams
|
|
61
|
+
const teamsData = await queryLinear(`
|
|
62
|
+
query {
|
|
63
|
+
teams {
|
|
64
|
+
nodes {
|
|
65
|
+
id
|
|
66
|
+
key
|
|
67
|
+
name
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
`);
|
|
72
|
+
|
|
73
|
+
console.log(`\n๐ Found ${teamsData.teams.nodes.length} teams:`);
|
|
74
|
+
for (const team of teamsData.teams.nodes) {
|
|
75
|
+
console.log(` - ${team.key}: ${team.name}`);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Get ALL issues from all teams using pagination
|
|
79
|
+
let allIssues = [];
|
|
80
|
+
let hasNextPage = true;
|
|
81
|
+
let endCursor = null;
|
|
82
|
+
|
|
83
|
+
while (hasNextPage) {
|
|
84
|
+
const issuesData = await queryLinear(`
|
|
85
|
+
query($after: String) {
|
|
86
|
+
issues(first: 100, orderBy: updatedAt, after: $after) {
|
|
87
|
+
nodes {
|
|
88
|
+
id
|
|
89
|
+
identifier
|
|
90
|
+
title
|
|
91
|
+
description
|
|
92
|
+
state {
|
|
93
|
+
name
|
|
94
|
+
type
|
|
95
|
+
}
|
|
96
|
+
priority
|
|
97
|
+
priorityLabel
|
|
98
|
+
team {
|
|
99
|
+
key
|
|
100
|
+
name
|
|
101
|
+
}
|
|
102
|
+
assignee {
|
|
103
|
+
name
|
|
104
|
+
email
|
|
105
|
+
}
|
|
106
|
+
createdAt
|
|
107
|
+
updatedAt
|
|
108
|
+
url
|
|
109
|
+
}
|
|
110
|
+
pageInfo {
|
|
111
|
+
hasNextPage
|
|
112
|
+
endCursor
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
`, { after: endCursor });
|
|
117
|
+
|
|
118
|
+
allIssues = allIssues.concat(issuesData.issues.nodes);
|
|
119
|
+
hasNextPage = issuesData.issues.pageInfo.hasNextPage;
|
|
120
|
+
endCursor = issuesData.issues.pageInfo.endCursor;
|
|
121
|
+
|
|
122
|
+
if (hasNextPage) {
|
|
123
|
+
console.log(` Fetched ${allIssues.length} issues so far...`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const issuesData = { issues: { nodes: allIssues } };
|
|
128
|
+
|
|
129
|
+
console.log(`\n๐ฅ Found ${issuesData.issues.nodes.length} issues total`);
|
|
130
|
+
|
|
131
|
+
// Group by team
|
|
132
|
+
const issuesByTeam = {};
|
|
133
|
+
for (const issue of issuesData.issues.nodes) {
|
|
134
|
+
const teamKey = issue.team.key;
|
|
135
|
+
if (!issuesByTeam[teamKey]) {
|
|
136
|
+
issuesByTeam[teamKey] = [];
|
|
137
|
+
}
|
|
138
|
+
issuesByTeam[teamKey].push(issue);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
for (const [teamKey, issues] of Object.entries(issuesByTeam)) {
|
|
142
|
+
console.log(` ${teamKey}: ${issues.length} issues`);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Load local tasks
|
|
146
|
+
const tasksFile = path.join(__dirname, '..', '.stackmemory', 'tasks.jsonl');
|
|
147
|
+
let localTasks = [];
|
|
148
|
+
const localLinearIds = new Set();
|
|
149
|
+
|
|
150
|
+
if (isMirrorMode) {
|
|
151
|
+
// In mirror mode, we'll replace everything
|
|
152
|
+
console.log('\n๐งน Mirror mode: Clearing existing tasks...');
|
|
153
|
+
if (fs.existsSync(tasksFile)) {
|
|
154
|
+
fs.writeFileSync(tasksFile, ''); // Clear the file
|
|
155
|
+
}
|
|
156
|
+
} else if (fs.existsSync(tasksFile)) {
|
|
157
|
+
const lines = fs.readFileSync(tasksFile, 'utf8').split('\n').filter(l => l.trim());
|
|
158
|
+
for (const line of lines) {
|
|
159
|
+
try {
|
|
160
|
+
const task = JSON.parse(line);
|
|
161
|
+
if (task.type === 'task_create' || task.type === 'task_update') {
|
|
162
|
+
localTasks.push(task);
|
|
163
|
+
const match = task.title?.match(/\[(STA-\d+|ENG-\d+)\]/);
|
|
164
|
+
if (match) {
|
|
165
|
+
localLinearIds.add(match[1]);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
} catch (e) {
|
|
169
|
+
// Skip invalid lines
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
console.log(`\n๐ Local tasks: ${localTasks.length}`);
|
|
175
|
+
console.log(`๐ Linear issues: ${issuesData.issues.nodes.length}`);
|
|
176
|
+
|
|
177
|
+
// In mirror mode, sync ALL issues; otherwise just missing ones
|
|
178
|
+
const issuesToSync = isMirrorMode ? issuesData.issues.nodes : [];
|
|
179
|
+
|
|
180
|
+
if (!isMirrorMode) {
|
|
181
|
+
// Find issues not in local tasks
|
|
182
|
+
for (const issue of issuesData.issues.nodes) {
|
|
183
|
+
if (!localLinearIds.has(issue.identifier)) {
|
|
184
|
+
issuesToSync.push(issue);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (issuesToSync.length > 0) {
|
|
190
|
+
const modeText = isMirrorMode ? 'Mirroring ALL' : 'Adding new';
|
|
191
|
+
console.log(`\n๐ ${modeText} Linear issues (${issuesToSync.length}):`);
|
|
192
|
+
|
|
193
|
+
const newTasks = [];
|
|
194
|
+
const displayLimit = Math.min(issuesToSync.length, 20);
|
|
195
|
+
|
|
196
|
+
for (let i = 0; i < issuesToSync.length; i++) {
|
|
197
|
+
const issue = issuesToSync[i];
|
|
198
|
+
|
|
199
|
+
if (i < displayLimit) {
|
|
200
|
+
console.log(` - ${issue.identifier}: ${issue.title}`);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const taskId = `tsk-${Math.random().toString(36).substr(2, 8)}`;
|
|
204
|
+
const task = {
|
|
205
|
+
id: taskId,
|
|
206
|
+
type: 'task_create',
|
|
207
|
+
timestamp: Date.now(),
|
|
208
|
+
frame_id: 'linear-sync',
|
|
209
|
+
title: `[${issue.identifier}] ${issue.title}`,
|
|
210
|
+
description: issue.description || '',
|
|
211
|
+
status: mapLinearState(issue.state.type),
|
|
212
|
+
priority: mapLinearPriority(issue.priority),
|
|
213
|
+
created_at: Date.now(),
|
|
214
|
+
depends_on: [],
|
|
215
|
+
blocks: [],
|
|
216
|
+
tags: ['linear', 'synced', issue.team.key.toLowerCase()],
|
|
217
|
+
context_score: 0.5,
|
|
218
|
+
external_refs: {
|
|
219
|
+
linear_id: issue.id,
|
|
220
|
+
linear_identifier: issue.identifier,
|
|
221
|
+
linear_url: issue.url,
|
|
222
|
+
team: issue.team.key,
|
|
223
|
+
assignee: issue.assignee?.email || null,
|
|
224
|
+
state_name: issue.state.name
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
newTasks.push(JSON.stringify(task));
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (issuesToSync.length > displayLimit) {
|
|
232
|
+
console.log(` ... and ${issuesToSync.length - displayLimit} more`);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (newTasks.length > 0) {
|
|
236
|
+
console.log(`\n๐พ Writing ${newTasks.length} tasks to local storage...`);
|
|
237
|
+
fs.appendFileSync(tasksFile, newTasks.join('\n') + '\n');
|
|
238
|
+
console.log('โ
Tasks synced successfully!');
|
|
239
|
+
}
|
|
240
|
+
} else if (!isMirrorMode) {
|
|
241
|
+
console.log('\nโ
All Linear issues already exist locally');
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Find local tasks not in Linear
|
|
245
|
+
const linearIds = new Set(issuesData.issues.nodes.map(i => i.identifier));
|
|
246
|
+
const missingInLinear = localTasks.filter(task => {
|
|
247
|
+
const match = task.title?.match(/\[(STA-\d+|ENG-\d+)\]/);
|
|
248
|
+
return match && !linearIds.has(match[1]);
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
if (missingInLinear.length > 0) {
|
|
252
|
+
console.log(`\n๐ค Local tasks not in Linear (${missingInLinear.length}):`);
|
|
253
|
+
for (const task of missingInLinear.slice(0, 10)) {
|
|
254
|
+
const match = task.title?.match(/\[(STA-\d+|ENG-\d+)\]/);
|
|
255
|
+
console.log(` - ${match?.[1] || 'Unknown'}: ${task.title}`);
|
|
256
|
+
}
|
|
257
|
+
if (missingInLinear.length > 10) {
|
|
258
|
+
console.log(` ... and ${missingInLinear.length - 10} more`);
|
|
259
|
+
}
|
|
260
|
+
console.log('\n๐ก These may be deleted Linear issues or local-only tasks');
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// Summary
|
|
264
|
+
console.log('\n๐ Sync Summary:');
|
|
265
|
+
console.log(` Total Linear issues: ${issuesData.issues.nodes.length}`);
|
|
266
|
+
console.log(` Total local tasks: ${localTasks.length}`);
|
|
267
|
+
if (!isMirrorMode) {
|
|
268
|
+
console.log(` Added to local: ${issuesToSync.length}`);
|
|
269
|
+
console.log(` Local-only tasks: ${missingInLinear.length}`);
|
|
270
|
+
} else {
|
|
271
|
+
console.log(` Mirrored: ${issuesToSync.length} tasks`);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
} catch (error) {
|
|
275
|
+
console.error('โ Error syncing with Linear:', error.message);
|
|
276
|
+
if (error.message.includes('authentication') || error.message.includes('401')) {
|
|
277
|
+
console.log('\n๐ Authentication failed. Please check your LINEAR_API_KEY');
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function mapLinearState(state) {
|
|
283
|
+
switch (state) {
|
|
284
|
+
case 'completed': return 'completed';
|
|
285
|
+
case 'started': return 'in_progress';
|
|
286
|
+
case 'canceled': return 'cancelled';
|
|
287
|
+
case 'cancelled': return 'cancelled';
|
|
288
|
+
default: return 'pending';
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function mapLinearPriority(priority) {
|
|
293
|
+
switch (priority) {
|
|
294
|
+
case 0: return 'none';
|
|
295
|
+
case 1: return 'urgent';
|
|
296
|
+
case 2: return 'high';
|
|
297
|
+
case 3: return 'medium';
|
|
298
|
+
case 4: return 'low';
|
|
299
|
+
default: return 'medium';
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
syncLinearTasks();
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { LinearClient } from '@linear/sdk';
|
|
4
|
+
import fs from 'fs';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import { fileURLToPath } from 'url';
|
|
7
|
+
import dotenv from 'dotenv';
|
|
8
|
+
|
|
9
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
10
|
+
|
|
11
|
+
// Load environment variables from .env file
|
|
12
|
+
dotenv.config({ path: path.join(__dirname, '..', '.env') });
|
|
13
|
+
|
|
14
|
+
async function syncLinearTasks() {
|
|
15
|
+
const apiKey = process.env.LINEAR_API_KEY;
|
|
16
|
+
|
|
17
|
+
if (!apiKey) {
|
|
18
|
+
console.error('โ LINEAR_API_KEY not found in environment');
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
console.log('๐ Connecting to Linear with API key...');
|
|
23
|
+
// Linear SDK expects the API key without the "lin_api_" prefix in some versions
|
|
24
|
+
// But let's use the full key and pass it correctly
|
|
25
|
+
const linearClient = new LinearClient({
|
|
26
|
+
apiKey: apiKey,
|
|
27
|
+
headers: {
|
|
28
|
+
'Authorization': apiKey
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
// Test connection and get team info
|
|
34
|
+
console.log('๐ Fetching workspace info...');
|
|
35
|
+
const me = await linearClient.viewer;
|
|
36
|
+
console.log(`โ
Connected as: ${me.name || me.email}`);
|
|
37
|
+
|
|
38
|
+
// Get all teams
|
|
39
|
+
const teams = await linearClient.teams();
|
|
40
|
+
console.log(`\n๐ Found ${teams.nodes.length} teams:`);
|
|
41
|
+
|
|
42
|
+
for (const team of teams.nodes) {
|
|
43
|
+
console.log(` - ${team.key}: ${team.name}`);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Get issues from StackMemory team
|
|
47
|
+
const stackTeam = teams.nodes.find(t => t.key === 'STA' || t.key === 'STACK');
|
|
48
|
+
if (!stackTeam) {
|
|
49
|
+
console.log('\nโ ๏ธ No StackMemory team found (looking for STA or STACK)');
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
console.log(`\n๐ฅ Fetching issues from ${stackTeam.name} (${stackTeam.key})...`);
|
|
54
|
+
const issues = await linearClient.issues({
|
|
55
|
+
filter: {
|
|
56
|
+
team: { key: { eq: stackTeam.key } }
|
|
57
|
+
},
|
|
58
|
+
first: 100
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
console.log(`Found ${issues.nodes.length} issues\n`);
|
|
62
|
+
|
|
63
|
+
// Load local tasks
|
|
64
|
+
const tasksFile = path.join(__dirname, '..', '.stackmemory', 'tasks.jsonl');
|
|
65
|
+
const localTasks = [];
|
|
66
|
+
|
|
67
|
+
if (fs.existsSync(tasksFile)) {
|
|
68
|
+
const lines = fs.readFileSync(tasksFile, 'utf8').split('\n').filter(l => l.trim());
|
|
69
|
+
for (const line of lines) {
|
|
70
|
+
try {
|
|
71
|
+
const task = JSON.parse(line);
|
|
72
|
+
if (task.type === 'task_create' || task.type === 'task_update') {
|
|
73
|
+
localTasks.push(task);
|
|
74
|
+
}
|
|
75
|
+
} catch (e) {
|
|
76
|
+
// Skip invalid lines
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
console.log(`๐ Local tasks: ${localTasks.length}`);
|
|
82
|
+
console.log(`๐ Linear issues: ${issues.nodes.length}`);
|
|
83
|
+
|
|
84
|
+
// Find tasks that exist in Linear but not locally
|
|
85
|
+
const localLinearIds = new Set();
|
|
86
|
+
for (const task of localTasks) {
|
|
87
|
+
const match = task.title?.match(/\[(STA-\d+|ENG-\d+)\]/);
|
|
88
|
+
if (match) {
|
|
89
|
+
localLinearIds.add(match[1]);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const missingLocally = [];
|
|
94
|
+
for (const issue of issues.nodes) {
|
|
95
|
+
if (!localLinearIds.has(issue.identifier)) {
|
|
96
|
+
missingLocally.push(issue);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (missingLocally.length > 0) {
|
|
101
|
+
console.log(`\n๐ Issues in Linear but not in local tasks (${missingLocally.length}):`);
|
|
102
|
+
|
|
103
|
+
// Create task entries for missing issues
|
|
104
|
+
const newTasks = [];
|
|
105
|
+
for (const issue of missingLocally) {
|
|
106
|
+
console.log(` - ${issue.identifier}: ${issue.title}`);
|
|
107
|
+
|
|
108
|
+
const taskId = `tsk-${Math.random().toString(36).substr(2, 8)}`;
|
|
109
|
+
const task = {
|
|
110
|
+
id: taskId,
|
|
111
|
+
type: 'task_create',
|
|
112
|
+
timestamp: Date.now(),
|
|
113
|
+
frame_id: 'linear-sync',
|
|
114
|
+
title: `[${issue.identifier}] ${issue.title}`,
|
|
115
|
+
description: issue.description || '',
|
|
116
|
+
status: mapLinearState(issue.state.type),
|
|
117
|
+
priority: mapLinearPriority(issue.priority),
|
|
118
|
+
created_at: Date.now(),
|
|
119
|
+
depends_on: [],
|
|
120
|
+
blocks: [],
|
|
121
|
+
tags: ['linear', 'synced'],
|
|
122
|
+
context_score: 0.5,
|
|
123
|
+
external_refs: {
|
|
124
|
+
linear_id: issue.id,
|
|
125
|
+
linear_url: issue.url
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
newTasks.push(JSON.stringify(task));
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (newTasks.length > 0) {
|
|
133
|
+
console.log(`\n๐พ Adding ${newTasks.length} tasks to local storage...`);
|
|
134
|
+
fs.appendFileSync(tasksFile, '\n' + newTasks.join('\n') + '\n');
|
|
135
|
+
console.log('โ
Tasks synced successfully!');
|
|
136
|
+
}
|
|
137
|
+
} else {
|
|
138
|
+
console.log('\nโ
All Linear issues already exist locally');
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Find local tasks not in Linear
|
|
142
|
+
const linearIds = new Set(issues.nodes.map(i => i.identifier));
|
|
143
|
+
const missingInLinear = localTasks.filter(task => {
|
|
144
|
+
const match = task.title?.match(/\[(STA-\d+|ENG-\d+)\]/);
|
|
145
|
+
return match && !linearIds.has(match[1]);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
if (missingInLinear.length > 0) {
|
|
149
|
+
console.log(`\n๐ค Local tasks not in Linear (${missingInLinear.length}):`);
|
|
150
|
+
for (const task of missingInLinear.slice(0, 10)) {
|
|
151
|
+
console.log(` - ${task.title}`);
|
|
152
|
+
}
|
|
153
|
+
if (missingInLinear.length > 10) {
|
|
154
|
+
console.log(` ... and ${missingInLinear.length - 10} more`);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
} catch (error) {
|
|
159
|
+
console.error('โ Error syncing with Linear:', error.message);
|
|
160
|
+
if (error.message.includes('authentication') || error.message.includes('401')) {
|
|
161
|
+
console.log('\n๐ Authentication failed. Please check your LINEAR_API_KEY');
|
|
162
|
+
console.log('You can get an API key from: https://linear.app/settings/api');
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function mapLinearState(state) {
|
|
168
|
+
switch (state) {
|
|
169
|
+
case 'completed': return 'completed';
|
|
170
|
+
case 'started': return 'in_progress';
|
|
171
|
+
case 'cancelled': return 'cancelled';
|
|
172
|
+
default: return 'pending';
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function mapLinearPriority(priority) {
|
|
177
|
+
switch (priority) {
|
|
178
|
+
case 1: return 'urgent';
|
|
179
|
+
case 2: return 'high';
|
|
180
|
+
case 3: return 'medium';
|
|
181
|
+
case 4: return 'low';
|
|
182
|
+
default: return 'medium';
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
syncLinearTasks();
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
echo "๐งช Testing StackMemory Auto-Triggers Implementation"
|
|
4
|
+
echo "===================================================="
|
|
5
|
+
echo ""
|
|
6
|
+
|
|
7
|
+
# Check what's actually available
|
|
8
|
+
echo "๐ Checking available commands..."
|
|
9
|
+
stackmemory --help 2>/dev/null | grep -E "clear|handoff|workflow|monitor" || echo "New commands not yet available in built version"
|
|
10
|
+
|
|
11
|
+
echo ""
|
|
12
|
+
echo "๐ Checking source files exist..."
|
|
13
|
+
for file in "src/core/session/clear-survival.ts" \
|
|
14
|
+
"src/core/session/handoff-generator.ts" \
|
|
15
|
+
"src/core/frame/workflow-templates.ts" \
|
|
16
|
+
"src/core/monitoring/session-monitor.ts" \
|
|
17
|
+
"src/cli/commands/clear.ts" \
|
|
18
|
+
"src/cli/commands/workflow.ts" \
|
|
19
|
+
"src/cli/commands/monitor.ts"; do
|
|
20
|
+
if [ -f "/Users/jwu/Dev/stackmemory/$file" ]; then
|
|
21
|
+
echo "โ
$file exists"
|
|
22
|
+
else
|
|
23
|
+
echo "โ $file missing"
|
|
24
|
+
fi
|
|
25
|
+
done
|
|
26
|
+
|
|
27
|
+
echo ""
|
|
28
|
+
echo "๐ง Checking Claude hooks installation..."
|
|
29
|
+
for hook in "on-startup" "on-message" "on-clear" "on-exit"; do
|
|
30
|
+
if [ -f "$HOME/.claude/hooks/$hook" ]; then
|
|
31
|
+
echo "โ
Hook $hook installed"
|
|
32
|
+
head -2 "$HOME/.claude/hooks/$hook" | tail -1
|
|
33
|
+
else
|
|
34
|
+
echo "โ Hook $hook not found"
|
|
35
|
+
fi
|
|
36
|
+
done
|
|
37
|
+
|
|
38
|
+
echo ""
|
|
39
|
+
echo "โ๏ธ Checking configuration..."
|
|
40
|
+
if [ -f ".stackmemory/config.json" ]; then
|
|
41
|
+
echo "โ
Config exists:"
|
|
42
|
+
cat .stackmemory/config.json | jq '.monitor, .clearSurvival, .handoff' 2>/dev/null || cat .stackmemory/config.json
|
|
43
|
+
else
|
|
44
|
+
echo "โ No config file"
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
echo ""
|
|
48
|
+
echo "๐ Status Summary:"
|
|
49
|
+
echo "- Source files: All created successfully โ
"
|
|
50
|
+
echo "- Build status: Not yet compiled (needs npm run build)"
|
|
51
|
+
echo "- Hooks: Installed in ~/.claude/hooks/ โ
"
|
|
52
|
+
echo "- Config: Auto-trigger settings configured โ
"
|
|
53
|
+
echo ""
|
|
54
|
+
echo "๐ Next Steps:"
|
|
55
|
+
echo "1. Build the project: cd /Users/jwu/Dev/stackmemory && npm run build"
|
|
56
|
+
echo "2. Test commands: stackmemory clear --status"
|
|
57
|
+
echo "3. Start monitor: stackmemory monitor --start"
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Browser MCP Test Script
|
|
5
|
+
* Tests the Browser MCP integration for browser automation
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
9
|
+
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
|
|
10
|
+
|
|
11
|
+
async function testBrowserMCP() {
|
|
12
|
+
console.log('๐ Testing Browser MCP Server...\n');
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
// Create client transport
|
|
16
|
+
const transport = new StdioClientTransport({
|
|
17
|
+
command: 'node',
|
|
18
|
+
args: ['/opt/homebrew/lib/node_modules/@browsermcp/mcp/dist/index.js'],
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
// Create MCP client
|
|
22
|
+
const client = new Client({
|
|
23
|
+
name: 'browser-mcp-test',
|
|
24
|
+
version: '1.0.0',
|
|
25
|
+
}, {
|
|
26
|
+
capabilities: {},
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
// Connect to server
|
|
30
|
+
await client.connect(transport);
|
|
31
|
+
console.log('โ
Connected to Browser MCP server\n');
|
|
32
|
+
|
|
33
|
+
// List available tools
|
|
34
|
+
const tools = await client.listTools();
|
|
35
|
+
console.log('๐ฆ Available tools:', tools.tools?.length || 0);
|
|
36
|
+
if (tools.tools) {
|
|
37
|
+
tools.tools.forEach(tool => {
|
|
38
|
+
console.log(` - ${tool.name}: ${tool.description}`);
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
console.log();
|
|
42
|
+
|
|
43
|
+
// Example: Navigate to a page
|
|
44
|
+
console.log('๐งช Testing browser navigation...');
|
|
45
|
+
try {
|
|
46
|
+
const result = await client.callTool('browser_navigate', {
|
|
47
|
+
url: 'https://example.com'
|
|
48
|
+
});
|
|
49
|
+
console.log('โ
Navigation successful:', result);
|
|
50
|
+
} catch (error) {
|
|
51
|
+
console.log('โ ๏ธ Navigation test failed:', error.message);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Example: Take screenshot
|
|
55
|
+
console.log('\n๐งช Testing screenshot capture...');
|
|
56
|
+
try {
|
|
57
|
+
const screenshot = await client.callTool('browser_screenshot', {});
|
|
58
|
+
console.log('โ
Screenshot captured');
|
|
59
|
+
} catch (error) {
|
|
60
|
+
console.log('โ ๏ธ Screenshot test failed:', error.message);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Close connection
|
|
64
|
+
await client.close();
|
|
65
|
+
console.log('\nโ
Browser MCP test complete!');
|
|
66
|
+
|
|
67
|
+
} catch (error) {
|
|
68
|
+
console.error('โ Error testing Browser MCP:', error);
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Run test
|
|
74
|
+
testBrowserMCP().catch(console.error);
|