specweave 0.23.10 → 0.23.14
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/.claude-plugin/marketplace.json +7 -7
- package/CLAUDE.md +384 -1449
- package/dist/src/cli/commands/cleanup-cache.d.ts +14 -0
- package/dist/src/cli/commands/cleanup-cache.d.ts.map +1 -0
- package/dist/src/cli/commands/cleanup-cache.js +63 -0
- package/dist/src/cli/commands/cleanup-cache.js.map +1 -0
- package/dist/src/cli/commands/init.js +40 -0
- package/dist/src/cli/commands/init.js.map +1 -1
- package/dist/src/cli/helpers/async-project-loader.d.ts +148 -0
- package/dist/src/cli/helpers/async-project-loader.d.ts.map +1 -0
- package/dist/src/cli/helpers/async-project-loader.js +351 -0
- package/dist/src/cli/helpers/async-project-loader.js.map +1 -0
- package/dist/src/cli/helpers/cancelation-handler.d.ts +123 -0
- package/dist/src/cli/helpers/cancelation-handler.d.ts.map +1 -0
- package/dist/src/cli/helpers/cancelation-handler.js +187 -0
- package/dist/src/cli/helpers/cancelation-handler.js.map +1 -0
- package/dist/src/cli/helpers/import-strategy-prompter.d.ts +43 -0
- package/dist/src/cli/helpers/import-strategy-prompter.d.ts.map +1 -0
- package/dist/src/cli/helpers/import-strategy-prompter.js +136 -0
- package/dist/src/cli/helpers/import-strategy-prompter.js.map +1 -0
- package/dist/src/cli/helpers/issue-tracker/ado.d.ts +5 -2
- package/dist/src/cli/helpers/issue-tracker/ado.d.ts.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/ado.js +90 -40
- package/dist/src/cli/helpers/issue-tracker/ado.js.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/jira.d.ts +2 -1
- package/dist/src/cli/helpers/issue-tracker/jira.d.ts.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/jira.js +120 -35
- package/dist/src/cli/helpers/issue-tracker/jira.js.map +1 -1
- package/dist/src/cli/helpers/progress-tracker.d.ts +121 -0
- package/dist/src/cli/helpers/progress-tracker.d.ts.map +1 -0
- package/dist/src/cli/helpers/progress-tracker.js +202 -0
- package/dist/src/cli/helpers/progress-tracker.js.map +1 -0
- package/dist/src/cli/helpers/project-count-fetcher.d.ts +69 -0
- package/dist/src/cli/helpers/project-count-fetcher.d.ts.map +1 -0
- package/dist/src/cli/helpers/project-count-fetcher.js +173 -0
- package/dist/src/cli/helpers/project-count-fetcher.js.map +1 -0
- package/dist/src/config/types.d.ts +14 -14
- package/dist/src/core/cache/cache-manager.d.ts +119 -0
- package/dist/src/core/cache/cache-manager.d.ts.map +1 -0
- package/dist/src/core/cache/cache-manager.js +304 -0
- package/dist/src/core/cache/cache-manager.js.map +1 -0
- package/dist/src/core/cache/rate-limit-checker.d.ts +92 -0
- package/dist/src/core/cache/rate-limit-checker.d.ts.map +1 -0
- package/dist/src/core/cache/rate-limit-checker.js +160 -0
- package/dist/src/core/cache/rate-limit-checker.js.map +1 -0
- package/dist/src/core/progress/cancelation-handler.d.ts +79 -0
- package/dist/src/core/progress/cancelation-handler.d.ts.map +1 -0
- package/dist/src/core/progress/cancelation-handler.js +111 -0
- package/dist/src/core/progress/cancelation-handler.js.map +1 -0
- package/dist/src/core/progress/error-logger.d.ts +58 -0
- package/dist/src/core/progress/error-logger.d.ts.map +1 -0
- package/dist/src/core/progress/error-logger.js +99 -0
- package/dist/src/core/progress/error-logger.js.map +1 -0
- package/dist/src/core/progress/import-state.d.ts +71 -0
- package/dist/src/core/progress/import-state.d.ts.map +1 -0
- package/dist/src/core/progress/import-state.js +96 -0
- package/dist/src/core/progress/import-state.js.map +1 -0
- package/dist/src/core/progress/progress-tracker.d.ts +139 -0
- package/dist/src/core/progress/progress-tracker.d.ts.map +1 -0
- package/dist/src/core/progress/progress-tracker.js +223 -0
- package/dist/src/core/progress/progress-tracker.js.map +1 -0
- package/dist/src/init/architecture/types.d.ts +6 -6
- package/dist/src/integrations/ado/ado-client.d.ts +25 -0
- package/dist/src/integrations/ado/ado-client.d.ts.map +1 -1
- package/dist/src/integrations/ado/ado-client.js +67 -0
- package/dist/src/integrations/ado/ado-client.js.map +1 -1
- package/dist/src/integrations/ado/ado-dependency-loader.d.ts +99 -0
- package/dist/src/integrations/ado/ado-dependency-loader.d.ts.map +1 -0
- package/dist/src/integrations/ado/ado-dependency-loader.js +207 -0
- package/dist/src/integrations/ado/ado-dependency-loader.js.map +1 -0
- package/dist/src/integrations/jira/jira-client.d.ts +32 -0
- package/dist/src/integrations/jira/jira-client.d.ts.map +1 -1
- package/dist/src/integrations/jira/jira-client.js +81 -0
- package/dist/src/integrations/jira/jira-client.js.map +1 -1
- package/dist/src/integrations/jira/jira-dependency-loader.d.ts +101 -0
- package/dist/src/integrations/jira/jira-dependency-loader.d.ts.map +1 -0
- package/dist/src/integrations/jira/jira-dependency-loader.js +200 -0
- package/dist/src/integrations/jira/jira-dependency-loader.js.map +1 -0
- package/package.json +1 -1
- package/plugins/specweave/.claude-plugin/plugin.json +20 -0
- package/plugins/specweave/agents/architect/AGENT.md +100 -602
- package/plugins/specweave/agents/pm/AGENT.md +96 -597
- package/plugins/specweave/agents/pm/AGENT.md.bak +1893 -0
- package/plugins/specweave/agents/pm/AGENT.md.bak2 +1754 -0
- package/plugins/specweave/commands/check-hooks.md +257 -0
- package/plugins/specweave/hooks/docs-changed.sh +9 -1
- package/plugins/specweave/hooks/docs-changed.sh.backup +79 -0
- package/plugins/specweave/hooks/human-input-required.sh +9 -1
- package/plugins/specweave/hooks/human-input-required.sh.backup +75 -0
- package/plugins/specweave/hooks/post-edit-spec.sh +202 -31
- package/plugins/specweave/hooks/post-first-increment.sh.backup +61 -0
- package/plugins/specweave/hooks/post-increment-change.sh +6 -1
- package/plugins/specweave/hooks/post-increment-change.sh.backup +98 -0
- package/plugins/specweave/hooks/post-increment-completion.sh +6 -1
- package/plugins/specweave/hooks/post-increment-completion.sh.backup +231 -0
- package/plugins/specweave/hooks/post-increment-planning.sh +6 -1
- package/plugins/specweave/hooks/post-increment-planning.sh.backup +1048 -0
- package/plugins/specweave/hooks/post-increment-status-change.sh +6 -1
- package/plugins/specweave/hooks/post-increment-status-change.sh.backup +147 -0
- package/plugins/specweave/hooks/post-metadata-change.sh +7 -1
- package/plugins/specweave/hooks/post-spec-update.sh.backup +158 -0
- package/plugins/specweave/hooks/post-task-completion.sh +225 -228
- package/plugins/specweave/hooks/post-user-story-complete.sh.backup +179 -0
- package/plugins/specweave/hooks/post-write-spec.sh +207 -31
- package/plugins/specweave/hooks/pre-command-deduplication.sh.backup +83 -0
- package/plugins/specweave/hooks/pre-edit-spec.sh +151 -0
- package/plugins/specweave/hooks/pre-implementation.sh +9 -1
- package/plugins/specweave/hooks/pre-implementation.sh.backup +67 -0
- package/plugins/specweave/hooks/pre-task-completion.sh +14 -8
- package/plugins/specweave/hooks/pre-task-completion.sh.backup +194 -0
- package/plugins/specweave/hooks/pre-tool-use.sh +9 -1
- package/plugins/specweave/hooks/pre-tool-use.sh.backup +133 -0
- package/plugins/specweave/hooks/pre-write-spec.sh +151 -0
- package/plugins/specweave/hooks/test-pretooluse-env.sh +72 -0
- package/plugins/specweave/hooks/user-prompt-submit.sh.backup +386 -0
- package/plugins/specweave/skills/compliance-architecture/SKILL.md +374 -0
- package/plugins/specweave/skills/external-sync-wizard/SKILL.md +610 -0
- package/plugins/specweave/skills/pm-closure-validation/SKILL.md +541 -0
- package/plugins/specweave/skills/roadmap-planner/SKILL.md +473 -0
- package/plugins/specweave-ado/commands/refresh-cache.js +25 -0
- package/plugins/specweave-ado/commands/refresh-cache.ts +40 -0
- package/plugins/specweave-ado/hooks/post-living-docs-update.sh +9 -2
- package/plugins/specweave-ado/hooks/post-living-docs-update.sh.backup +353 -0
- package/plugins/specweave-ado/hooks/post-task-completion.sh +10 -2
- package/plugins/specweave-ado/hooks/post-task-completion.sh.backup +172 -0
- package/plugins/specweave-github/hooks/post-task-completion.sh +10 -2
- package/plugins/specweave-github/hooks/post-task-completion.sh.backup +258 -0
- package/plugins/specweave-jira/commands/refresh-cache.js +25 -0
- package/plugins/specweave-jira/commands/refresh-cache.ts +40 -0
- package/plugins/specweave-jira/hooks/post-task-completion.sh +10 -2
- package/plugins/specweave-jira/hooks/post-task-completion.sh.backup +172 -0
- package/plugins/specweave-kafka-streams/commands/topology.md +437 -0
- package/plugins/specweave-n8n/commands/workflow-template.md +262 -0
- package/plugins/specweave-release/hooks/.specweave/logs/dora-tracking.log +252 -6465
- package/plugins/specweave-release/hooks/post-task-completion.sh.backup +110 -0
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
import { promises as fs } from 'fs';
|
|
2
|
+
import { existsSync } from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { consoleLogger } from '../../utils/logger.js';
|
|
5
|
+
/**
|
|
6
|
+
* CacheManager - Manages file-based caching with TTL validation
|
|
7
|
+
*
|
|
8
|
+
* Features:
|
|
9
|
+
* - 24-hour TTL by default (configurable)
|
|
10
|
+
* - Atomic writes (temp file + rename)
|
|
11
|
+
* - Corruption detection and auto-recovery
|
|
12
|
+
* - Per-project cache separation
|
|
13
|
+
* - Thread-safe operations
|
|
14
|
+
*
|
|
15
|
+
* Cache file format:
|
|
16
|
+
* {
|
|
17
|
+
* "data": <T>,
|
|
18
|
+
* "timestamp": 1700000000000,
|
|
19
|
+
* "ttl": 86400000
|
|
20
|
+
* }
|
|
21
|
+
*/
|
|
22
|
+
export class CacheManager {
|
|
23
|
+
constructor(projectRoot, options = {}) {
|
|
24
|
+
this.defaultTTL = 24 * 60 * 60 * 1000; // 24 hours in milliseconds
|
|
25
|
+
this.cacheDir = path.join(projectRoot, '.specweave', 'cache');
|
|
26
|
+
this.logger = options.logger ?? consoleLogger;
|
|
27
|
+
if (options.ttl) {
|
|
28
|
+
this.defaultTTL = options.ttl;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Get cached data if valid (within TTL)
|
|
33
|
+
*
|
|
34
|
+
* @param key Cache key (e.g., "jira-projects", "jira-BACKEND-deps")
|
|
35
|
+
* @returns Cached data or null if cache miss/expired/corrupted
|
|
36
|
+
*/
|
|
37
|
+
async get(key) {
|
|
38
|
+
const filePath = this.getCachePath(key);
|
|
39
|
+
if (!existsSync(filePath)) {
|
|
40
|
+
this.logger.log(`Cache miss: ${key}`);
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
45
|
+
const cached = JSON.parse(content);
|
|
46
|
+
// Validate cache structure
|
|
47
|
+
if (!this.isValidCacheStructure(cached)) {
|
|
48
|
+
this.logger.error(`Invalid cache structure for ${key}, deleting...`);
|
|
49
|
+
await this.delete(key);
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
// Check TTL
|
|
53
|
+
if (!this.isValid(cached)) {
|
|
54
|
+
const age = Date.now() - cached.timestamp;
|
|
55
|
+
const ageHours = (age / (1000 * 60 * 60)).toFixed(1);
|
|
56
|
+
this.logger.log(`Cache expired: ${key} (age: ${ageHours}h, TTL: ${cached.ttl / (1000 * 60 * 60)}h)`);
|
|
57
|
+
// Don't delete expired cache immediately - might be used as stale fallback
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
const remaining = cached.timestamp + cached.ttl - Date.now();
|
|
61
|
+
const remainingHours = (remaining / (1000 * 60 * 60)).toFixed(1);
|
|
62
|
+
this.logger.log(`Cache hit: ${key} (TTL remaining: ${remainingHours}h)`);
|
|
63
|
+
return cached.data;
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
// Corruption detected
|
|
67
|
+
this.logger.error(`Cache corruption detected for ${key}: ${error.message}`);
|
|
68
|
+
await this.logCacheError(key, error);
|
|
69
|
+
await this.delete(key);
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Get cached data even if expired (stale cache fallback)
|
|
75
|
+
*
|
|
76
|
+
* Used for rate limit fallback: when API rate limit hit,
|
|
77
|
+
* use stale cache instead of failing
|
|
78
|
+
*
|
|
79
|
+
* @param key Cache key
|
|
80
|
+
* @returns Cached data or null if missing/corrupted (ignores TTL)
|
|
81
|
+
*/
|
|
82
|
+
async getStale(key) {
|
|
83
|
+
const filePath = this.getCachePath(key);
|
|
84
|
+
if (!existsSync(filePath)) {
|
|
85
|
+
this.logger.log(`No stale cache available: ${key}`);
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
try {
|
|
89
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
90
|
+
const cached = JSON.parse(content);
|
|
91
|
+
// Validate structure only (ignore TTL)
|
|
92
|
+
if (!this.isValidCacheStructure(cached)) {
|
|
93
|
+
this.logger.error(`Invalid cache structure for ${key}, deleting...`);
|
|
94
|
+
await this.delete(key);
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
const age = Date.now() - cached.timestamp;
|
|
98
|
+
const ageHours = (age / (1000 * 60 * 60)).toFixed(1);
|
|
99
|
+
const ttlHours = cached.ttl / (1000 * 60 * 60);
|
|
100
|
+
const expiredHoursAgo = (parseFloat(ageHours) - ttlHours).toFixed(1);
|
|
101
|
+
this.logger.warn(`Using stale cache: ${key} (age: ${ageHours}h, expired ${expiredHoursAgo}h ago)`);
|
|
102
|
+
return cached.data;
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
this.logger.error(`Failed to read stale cache for ${key}: ${error.message}`);
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Set cached data with current timestamp and TTL
|
|
111
|
+
*
|
|
112
|
+
* Uses atomic write pattern (temp file + rename) to prevent corruption
|
|
113
|
+
*
|
|
114
|
+
* @param key Cache key
|
|
115
|
+
* @param data Data to cache
|
|
116
|
+
* @param ttl Optional custom TTL (overrides default)
|
|
117
|
+
*/
|
|
118
|
+
async set(key, data, ttl) {
|
|
119
|
+
await this.ensureCacheDir();
|
|
120
|
+
const filePath = this.getCachePath(key);
|
|
121
|
+
const tempPath = `${filePath}.tmp`;
|
|
122
|
+
const cached = {
|
|
123
|
+
data,
|
|
124
|
+
timestamp: Date.now(),
|
|
125
|
+
ttl: ttl ?? this.defaultTTL,
|
|
126
|
+
};
|
|
127
|
+
try {
|
|
128
|
+
// Write to temp file first (atomic write pattern)
|
|
129
|
+
await fs.writeFile(tempPath, JSON.stringify(cached, null, 2), 'utf-8');
|
|
130
|
+
// Rename temp file to final name (atomic operation)
|
|
131
|
+
await fs.rename(tempPath, filePath);
|
|
132
|
+
this.logger.log(`Cache set: ${key} (TTL: ${cached.ttl / (1000 * 60 * 60)}h)`);
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
this.logger.error(`Failed to write cache for ${key}: ${error.message}`);
|
|
136
|
+
// Cleanup temp file if exists
|
|
137
|
+
if (existsSync(tempPath)) {
|
|
138
|
+
await fs.unlink(tempPath).catch(() => { });
|
|
139
|
+
}
|
|
140
|
+
throw error;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Delete cached data
|
|
145
|
+
*
|
|
146
|
+
* @param key Cache key
|
|
147
|
+
*/
|
|
148
|
+
async delete(key) {
|
|
149
|
+
const filePath = this.getCachePath(key);
|
|
150
|
+
if (existsSync(filePath)) {
|
|
151
|
+
await fs.unlink(filePath);
|
|
152
|
+
this.logger.log(`Cache deleted: ${key}`);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Clear all cached data
|
|
157
|
+
*/
|
|
158
|
+
async clearAll() {
|
|
159
|
+
if (!existsSync(this.cacheDir)) {
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
const files = await fs.readdir(this.cacheDir);
|
|
163
|
+
const jsonFiles = files.filter(f => f.endsWith('.json'));
|
|
164
|
+
for (const file of jsonFiles) {
|
|
165
|
+
const filePath = path.join(this.cacheDir, file);
|
|
166
|
+
await fs.unlink(filePath);
|
|
167
|
+
}
|
|
168
|
+
this.logger.log(`Cleared ${jsonFiles.length} cache files`);
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Get cache statistics for monitoring
|
|
172
|
+
*/
|
|
173
|
+
async getStats() {
|
|
174
|
+
const stats = {
|
|
175
|
+
totalFiles: 0,
|
|
176
|
+
totalSize: 0,
|
|
177
|
+
oldestCache: null,
|
|
178
|
+
oldestCacheAge: null,
|
|
179
|
+
providers: {},
|
|
180
|
+
};
|
|
181
|
+
if (!existsSync(this.cacheDir)) {
|
|
182
|
+
return stats;
|
|
183
|
+
}
|
|
184
|
+
const files = await fs.readdir(this.cacheDir);
|
|
185
|
+
const jsonFiles = files.filter(f => f.endsWith('.json'));
|
|
186
|
+
let oldestTimestamp = Date.now();
|
|
187
|
+
let oldestFile = null;
|
|
188
|
+
for (const file of jsonFiles) {
|
|
189
|
+
const filePath = path.join(this.cacheDir, file);
|
|
190
|
+
const fileStats = await fs.stat(filePath);
|
|
191
|
+
stats.totalFiles++;
|
|
192
|
+
stats.totalSize += fileStats.size;
|
|
193
|
+
// Determine provider from filename (e.g., "jira-projects.json" → "jira")
|
|
194
|
+
const provider = file.split('-')[0];
|
|
195
|
+
if (!stats.providers[provider]) {
|
|
196
|
+
stats.providers[provider] = { files: 0, size: 0 };
|
|
197
|
+
}
|
|
198
|
+
stats.providers[provider].files++;
|
|
199
|
+
stats.providers[provider].size += fileStats.size;
|
|
200
|
+
// Find oldest cache
|
|
201
|
+
try {
|
|
202
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
203
|
+
const cached = JSON.parse(content);
|
|
204
|
+
if (cached.timestamp < oldestTimestamp) {
|
|
205
|
+
oldestTimestamp = cached.timestamp;
|
|
206
|
+
oldestFile = file;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
catch {
|
|
210
|
+
// Ignore corrupted files
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
if (oldestFile) {
|
|
214
|
+
stats.oldestCache = oldestFile;
|
|
215
|
+
stats.oldestCacheAge = (Date.now() - oldestTimestamp) / (1000 * 60 * 60); // hours
|
|
216
|
+
}
|
|
217
|
+
return stats;
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Delete caches older than specified age
|
|
221
|
+
*
|
|
222
|
+
* @param maxAgeMs Maximum age in milliseconds
|
|
223
|
+
* @returns Count of deleted caches
|
|
224
|
+
*/
|
|
225
|
+
async deleteOlderThan(maxAgeMs) {
|
|
226
|
+
if (!existsSync(this.cacheDir)) {
|
|
227
|
+
return 0;
|
|
228
|
+
}
|
|
229
|
+
const files = await fs.readdir(this.cacheDir);
|
|
230
|
+
const jsonFiles = files.filter(f => f.endsWith('.json'));
|
|
231
|
+
let deletedCount = 0;
|
|
232
|
+
const cutoffTime = Date.now() - maxAgeMs;
|
|
233
|
+
for (const file of jsonFiles) {
|
|
234
|
+
const filePath = path.join(this.cacheDir, file);
|
|
235
|
+
try {
|
|
236
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
237
|
+
const cached = JSON.parse(content);
|
|
238
|
+
if (cached.timestamp < cutoffTime) {
|
|
239
|
+
await fs.unlink(filePath);
|
|
240
|
+
deletedCount++;
|
|
241
|
+
this.logger.log(`Deleted old cache: ${file}`);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
catch {
|
|
245
|
+
// Delete corrupted files too
|
|
246
|
+
await fs.unlink(filePath);
|
|
247
|
+
deletedCount++;
|
|
248
|
+
this.logger.log(`Deleted corrupted cache: ${file}`);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
return deletedCount;
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Check if cached data is still valid (within TTL)
|
|
255
|
+
*/
|
|
256
|
+
isValid(cached) {
|
|
257
|
+
const now = Date.now();
|
|
258
|
+
const age = now - cached.timestamp;
|
|
259
|
+
return age < cached.ttl;
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Validate cache structure has required fields
|
|
263
|
+
*/
|
|
264
|
+
isValidCacheStructure(cached) {
|
|
265
|
+
return (cached &&
|
|
266
|
+
typeof cached === 'object' &&
|
|
267
|
+
'data' in cached &&
|
|
268
|
+
'timestamp' in cached &&
|
|
269
|
+
'ttl' in cached &&
|
|
270
|
+
typeof cached.timestamp === 'number' &&
|
|
271
|
+
typeof cached.ttl === 'number');
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Get full cache file path for a key
|
|
275
|
+
*/
|
|
276
|
+
getCachePath(key) {
|
|
277
|
+
return path.join(this.cacheDir, `${key}.json`);
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Ensure cache directory exists
|
|
281
|
+
*/
|
|
282
|
+
async ensureCacheDir() {
|
|
283
|
+
if (!existsSync(this.cacheDir)) {
|
|
284
|
+
await fs.mkdir(this.cacheDir, { recursive: true });
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Log cache errors to dedicated error log
|
|
289
|
+
*/
|
|
290
|
+
async logCacheError(key, error) {
|
|
291
|
+
const logDir = path.join(path.dirname(this.cacheDir), 'logs');
|
|
292
|
+
const logPath = path.join(logDir, 'cache-errors.log');
|
|
293
|
+
try {
|
|
294
|
+
await fs.mkdir(logDir, { recursive: true });
|
|
295
|
+
const timestamp = new Date().toISOString();
|
|
296
|
+
const logEntry = `[${timestamp}] Cache error for ${key}: ${error.message}\n`;
|
|
297
|
+
await fs.appendFile(logPath, logEntry, 'utf-8');
|
|
298
|
+
}
|
|
299
|
+
catch {
|
|
300
|
+
// Ignore logging errors
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
//# sourceMappingURL=cache-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache-manager.js","sourceRoot":"","sources":["../../../../src/core/cache/cache-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { Logger } from '../../utils/logger.js';
|
|
2
|
+
/**
|
|
3
|
+
* Rate limit response headers (JIRA, GitHub, ADO)
|
|
4
|
+
*/
|
|
5
|
+
export interface RateLimitHeaders {
|
|
6
|
+
remaining?: number;
|
|
7
|
+
reset?: number;
|
|
8
|
+
retryAfter?: number;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Rate limit check result
|
|
12
|
+
*/
|
|
13
|
+
export interface RateLimitCheckResult {
|
|
14
|
+
canProceed: boolean;
|
|
15
|
+
reason?: string;
|
|
16
|
+
retryAfter?: number;
|
|
17
|
+
remaining?: number;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* RateLimitChecker - Detects API rate limits and prevents hitting limits
|
|
21
|
+
*
|
|
22
|
+
* Features:
|
|
23
|
+
* - Parses rate limit headers from JIRA, GitHub, ADO
|
|
24
|
+
* - Handles 429 (Too Many Requests) errors
|
|
25
|
+
* - Suggests using stale cache when rate limit low
|
|
26
|
+
* - Supports exponential backoff
|
|
27
|
+
*
|
|
28
|
+
* Supported headers:
|
|
29
|
+
* - X-RateLimit-Remaining (JIRA Cloud, GitHub)
|
|
30
|
+
* - X-RateLimit-Reset (JIRA Cloud, GitHub)
|
|
31
|
+
* - Retry-After (Standard HTTP, JIRA Server, ADO)
|
|
32
|
+
* - x-ms-ratelimit-remaining (Azure DevOps)
|
|
33
|
+
*
|
|
34
|
+
* Threshold: 10 requests remaining (configurable)
|
|
35
|
+
*/
|
|
36
|
+
export declare class RateLimitChecker {
|
|
37
|
+
private logger;
|
|
38
|
+
private threshold;
|
|
39
|
+
constructor(options?: {
|
|
40
|
+
logger?: Logger;
|
|
41
|
+
threshold?: number;
|
|
42
|
+
});
|
|
43
|
+
/**
|
|
44
|
+
* Check if safe to proceed with API call based on response headers
|
|
45
|
+
*
|
|
46
|
+
* @param headers Response headers from previous API call
|
|
47
|
+
* @returns RateLimitCheckResult with canProceed flag
|
|
48
|
+
*/
|
|
49
|
+
shouldProceed(headers: RateLimitHeaders): RateLimitCheckResult;
|
|
50
|
+
/**
|
|
51
|
+
* Handle 429 (Too Many Requests) error
|
|
52
|
+
*
|
|
53
|
+
* @param error Error object with response headers
|
|
54
|
+
* @returns Promise<void>
|
|
55
|
+
*/
|
|
56
|
+
handleRateLimitError(error: any): Promise<void>;
|
|
57
|
+
/**
|
|
58
|
+
* Extract rate limit headers from HTTP response
|
|
59
|
+
*
|
|
60
|
+
* Supports multiple header formats:
|
|
61
|
+
* - JIRA Cloud: X-RateLimit-Remaining, X-RateLimit-Reset
|
|
62
|
+
* - GitHub: X-RateLimit-Remaining, X-RateLimit-Reset
|
|
63
|
+
* - ADO: x-ms-ratelimit-remaining-resource, Retry-After
|
|
64
|
+
* - Standard: Retry-After
|
|
65
|
+
*
|
|
66
|
+
* @param response HTTP response or response-like object
|
|
67
|
+
* @returns RateLimitHeaders object
|
|
68
|
+
*/
|
|
69
|
+
extractHeaders(response: any): RateLimitHeaders;
|
|
70
|
+
/**
|
|
71
|
+
* Extract Retry-After from error response
|
|
72
|
+
*
|
|
73
|
+
* @param error Error object
|
|
74
|
+
* @returns Retry-After in seconds, or null
|
|
75
|
+
*/
|
|
76
|
+
private extractRetryAfter;
|
|
77
|
+
/**
|
|
78
|
+
* Extract X-RateLimit-Reset from error response
|
|
79
|
+
*
|
|
80
|
+
* @param error Error object
|
|
81
|
+
* @returns Reset timestamp (Unix seconds), or null
|
|
82
|
+
*/
|
|
83
|
+
private extractReset;
|
|
84
|
+
/**
|
|
85
|
+
* Check if error is a rate limit error (429 status)
|
|
86
|
+
*
|
|
87
|
+
* @param error Error object
|
|
88
|
+
* @returns True if 429 error
|
|
89
|
+
*/
|
|
90
|
+
isRateLimitError(error: any): boolean;
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=rate-limit-checker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limit-checker.d.ts","sourceRoot":"","sources":["../../../../src/core/cache/rate-limit-checker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAiB,MAAM,uBAAuB,CAAC;AAE9D;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,UAAU,EAAE,OAAO,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,SAAS,CAAS;gBAEd,OAAO,GAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAO;IAKjE;;;;;OAKG;IACH,aAAa,CAAC,OAAO,EAAE,gBAAgB,GAAG,oBAAoB;IA4B9D;;;;;OAKG;IACG,oBAAoB,CAAC,KAAK,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;IAiBrD;;;;;;;;;;;OAWG;IACH,cAAc,CAAC,QAAQ,EAAE,GAAG,GAAG,gBAAgB;IAsC/C;;;;;OAKG;IACH,OAAO,CAAC,iBAAiB;IAWzB;;;;;OAKG;IACH,OAAO,CAAC,YAAY;IAYpB;;;;;OAKG;IACH,gBAAgB,CAAC,KAAK,EAAE,GAAG,GAAG,OAAO;CAGtC"}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { consoleLogger } from '../../utils/logger.js';
|
|
2
|
+
/**
|
|
3
|
+
* RateLimitChecker - Detects API rate limits and prevents hitting limits
|
|
4
|
+
*
|
|
5
|
+
* Features:
|
|
6
|
+
* - Parses rate limit headers from JIRA, GitHub, ADO
|
|
7
|
+
* - Handles 429 (Too Many Requests) errors
|
|
8
|
+
* - Suggests using stale cache when rate limit low
|
|
9
|
+
* - Supports exponential backoff
|
|
10
|
+
*
|
|
11
|
+
* Supported headers:
|
|
12
|
+
* - X-RateLimit-Remaining (JIRA Cloud, GitHub)
|
|
13
|
+
* - X-RateLimit-Reset (JIRA Cloud, GitHub)
|
|
14
|
+
* - Retry-After (Standard HTTP, JIRA Server, ADO)
|
|
15
|
+
* - x-ms-ratelimit-remaining (Azure DevOps)
|
|
16
|
+
*
|
|
17
|
+
* Threshold: 10 requests remaining (configurable)
|
|
18
|
+
*/
|
|
19
|
+
export class RateLimitChecker {
|
|
20
|
+
constructor(options = {}) {
|
|
21
|
+
this.logger = options.logger ?? consoleLogger;
|
|
22
|
+
this.threshold = options.threshold ?? 10; // Default: warn when < 10 requests remaining
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Check if safe to proceed with API call based on response headers
|
|
26
|
+
*
|
|
27
|
+
* @param headers Response headers from previous API call
|
|
28
|
+
* @returns RateLimitCheckResult with canProceed flag
|
|
29
|
+
*/
|
|
30
|
+
shouldProceed(headers) {
|
|
31
|
+
const remaining = headers.remaining;
|
|
32
|
+
// No rate limit headers = assume safe to proceed
|
|
33
|
+
if (remaining === undefined) {
|
|
34
|
+
return { canProceed: true };
|
|
35
|
+
}
|
|
36
|
+
// Low rate limit = use stale cache instead
|
|
37
|
+
if (remaining < this.threshold) {
|
|
38
|
+
const reason = `Rate limit low (${remaining} requests remaining, threshold: ${this.threshold})`;
|
|
39
|
+
this.logger.warn(reason);
|
|
40
|
+
this.logger.warn('Suggestion: Use stale cache to avoid hitting rate limit');
|
|
41
|
+
return {
|
|
42
|
+
canProceed: false,
|
|
43
|
+
reason,
|
|
44
|
+
remaining,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
// Safe to proceed
|
|
48
|
+
return {
|
|
49
|
+
canProceed: true,
|
|
50
|
+
remaining,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Handle 429 (Too Many Requests) error
|
|
55
|
+
*
|
|
56
|
+
* @param error Error object with response headers
|
|
57
|
+
* @returns Promise<void>
|
|
58
|
+
*/
|
|
59
|
+
async handleRateLimitError(error) {
|
|
60
|
+
const retryAfter = this.extractRetryAfter(error);
|
|
61
|
+
const reset = this.extractReset(error);
|
|
62
|
+
if (retryAfter) {
|
|
63
|
+
this.logger.error(`Rate limit exceeded. Retry after ${retryAfter} seconds.`);
|
|
64
|
+
this.logger.warn('Suggestion: Use stale cache while waiting for rate limit reset');
|
|
65
|
+
}
|
|
66
|
+
else if (reset) {
|
|
67
|
+
const waitTime = Math.max(0, reset - Date.now() / 1000);
|
|
68
|
+
this.logger.error(`Rate limit exceeded. Reset in ${Math.ceil(waitTime)} seconds.`);
|
|
69
|
+
this.logger.warn('Suggestion: Use stale cache while waiting for rate limit reset');
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
this.logger.error('Rate limit exceeded (no retry time provided)');
|
|
73
|
+
this.logger.warn('Suggestion: Use stale cache and retry later');
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Extract rate limit headers from HTTP response
|
|
78
|
+
*
|
|
79
|
+
* Supports multiple header formats:
|
|
80
|
+
* - JIRA Cloud: X-RateLimit-Remaining, X-RateLimit-Reset
|
|
81
|
+
* - GitHub: X-RateLimit-Remaining, X-RateLimit-Reset
|
|
82
|
+
* - ADO: x-ms-ratelimit-remaining-resource, Retry-After
|
|
83
|
+
* - Standard: Retry-After
|
|
84
|
+
*
|
|
85
|
+
* @param response HTTP response or response-like object
|
|
86
|
+
* @returns RateLimitHeaders object
|
|
87
|
+
*/
|
|
88
|
+
extractHeaders(response) {
|
|
89
|
+
const headers = {};
|
|
90
|
+
// Handle different response formats (axios, fetch, etc.)
|
|
91
|
+
const getHeader = (name) => {
|
|
92
|
+
if (response.headers) {
|
|
93
|
+
// Axios-style headers object
|
|
94
|
+
if (typeof response.headers.get === 'function') {
|
|
95
|
+
return response.headers.get(name) ?? undefined;
|
|
96
|
+
}
|
|
97
|
+
// Plain object headers
|
|
98
|
+
return response.headers[name] ?? response.headers[name.toLowerCase()] ?? undefined;
|
|
99
|
+
}
|
|
100
|
+
return undefined;
|
|
101
|
+
};
|
|
102
|
+
// X-RateLimit-Remaining (JIRA Cloud, GitHub)
|
|
103
|
+
const remaining = getHeader('X-RateLimit-Remaining') || getHeader('x-ms-ratelimit-remaining-resource');
|
|
104
|
+
if (remaining) {
|
|
105
|
+
headers.remaining = parseInt(remaining, 10);
|
|
106
|
+
}
|
|
107
|
+
// X-RateLimit-Reset (JIRA Cloud, GitHub)
|
|
108
|
+
const reset = getHeader('X-RateLimit-Reset');
|
|
109
|
+
if (reset) {
|
|
110
|
+
headers.reset = parseInt(reset, 10);
|
|
111
|
+
}
|
|
112
|
+
// Retry-After (Standard HTTP, JIRA Server, ADO)
|
|
113
|
+
const retryAfter = getHeader('Retry-After');
|
|
114
|
+
if (retryAfter) {
|
|
115
|
+
headers.retryAfter = parseInt(retryAfter, 10);
|
|
116
|
+
}
|
|
117
|
+
return headers;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Extract Retry-After from error response
|
|
121
|
+
*
|
|
122
|
+
* @param error Error object
|
|
123
|
+
* @returns Retry-After in seconds, or null
|
|
124
|
+
*/
|
|
125
|
+
extractRetryAfter(error) {
|
|
126
|
+
if (error.response?.headers) {
|
|
127
|
+
const retryAfter = error.response.headers['Retry-After'] || error.response.headers['retry-after'];
|
|
128
|
+
if (retryAfter) {
|
|
129
|
+
return parseInt(retryAfter, 10);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Extract X-RateLimit-Reset from error response
|
|
136
|
+
*
|
|
137
|
+
* @param error Error object
|
|
138
|
+
* @returns Reset timestamp (Unix seconds), or null
|
|
139
|
+
*/
|
|
140
|
+
extractReset(error) {
|
|
141
|
+
if (error.response?.headers) {
|
|
142
|
+
const reset = error.response.headers['X-RateLimit-Reset'] ||
|
|
143
|
+
error.response.headers['x-ratelimit-reset'];
|
|
144
|
+
if (reset) {
|
|
145
|
+
return parseInt(reset, 10);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Check if error is a rate limit error (429 status)
|
|
152
|
+
*
|
|
153
|
+
* @param error Error object
|
|
154
|
+
* @returns True if 429 error
|
|
155
|
+
*/
|
|
156
|
+
isRateLimitError(error) {
|
|
157
|
+
return error.response?.status === 429 || error.status === 429;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
//# sourceMappingURL=rate-limit-checker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limit-checker.js","sourceRoot":"","sources":["../../../../src/core/cache/rate-limit-checker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CancelationHandler - Graceful Ctrl+C (SIGINT) handling with state persistence
|
|
3
|
+
*
|
|
4
|
+
* Features:
|
|
5
|
+
* - Single Ctrl+C: Save state and exit gracefully
|
|
6
|
+
* - Double Ctrl+C (within 2s): Force exit immediately
|
|
7
|
+
* - Polling mechanism for cooperative cancelation
|
|
8
|
+
* - State persistence to resume later
|
|
9
|
+
*
|
|
10
|
+
* @module core/progress/cancelation-handler
|
|
11
|
+
*/
|
|
12
|
+
import type { Logger } from '../../utils/logger.js';
|
|
13
|
+
/**
|
|
14
|
+
* Cancelation handler options
|
|
15
|
+
*/
|
|
16
|
+
export interface CancelationOptions {
|
|
17
|
+
/** State save callback - called when Ctrl+C is pressed */
|
|
18
|
+
onSaveState?: () => Promise<void>;
|
|
19
|
+
/** Logger instance */
|
|
20
|
+
logger?: Logger;
|
|
21
|
+
/** Force exit timeout in milliseconds (default: 2000ms) */
|
|
22
|
+
forceExitTimeout?: number;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* CancelationHandler - Handles SIGINT gracefully with double Ctrl+C force exit
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```typescript
|
|
29
|
+
* const handler = new CancelationHandler({
|
|
30
|
+
* onSaveState: async () => {
|
|
31
|
+
* await saveImportState();
|
|
32
|
+
* }
|
|
33
|
+
* });
|
|
34
|
+
*
|
|
35
|
+
* handler.register();
|
|
36
|
+
*
|
|
37
|
+
* // In import loop
|
|
38
|
+
* for (const project of projects) {
|
|
39
|
+
* if (handler.shouldCancel()) {
|
|
40
|
+
* break;
|
|
41
|
+
* }
|
|
42
|
+
* await importProject(project);
|
|
43
|
+
* }
|
|
44
|
+
*
|
|
45
|
+
* handler.unregister();
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
export declare class CancelationHandler {
|
|
49
|
+
private logger;
|
|
50
|
+
private onSaveState?;
|
|
51
|
+
private forceExitTimeout;
|
|
52
|
+
private cancelRequested;
|
|
53
|
+
private firstCtrlCTime;
|
|
54
|
+
private sigintHandler;
|
|
55
|
+
constructor(options?: CancelationOptions);
|
|
56
|
+
/**
|
|
57
|
+
* Register SIGINT listener
|
|
58
|
+
*/
|
|
59
|
+
register(): void;
|
|
60
|
+
/**
|
|
61
|
+
* Unregister SIGINT listener
|
|
62
|
+
*/
|
|
63
|
+
unregister(): void;
|
|
64
|
+
/**
|
|
65
|
+
* Check if cancelation was requested (polling mechanism)
|
|
66
|
+
*
|
|
67
|
+
* @returns true if Ctrl+C was pressed
|
|
68
|
+
*/
|
|
69
|
+
shouldCancel(): boolean;
|
|
70
|
+
/**
|
|
71
|
+
* Handle SIGINT signal (Ctrl+C)
|
|
72
|
+
*/
|
|
73
|
+
private handleSigint;
|
|
74
|
+
/**
|
|
75
|
+
* Reset cancelation state (useful for testing)
|
|
76
|
+
*/
|
|
77
|
+
reset(): void;
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=cancelation-handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cancelation-handler.d.ts","sourceRoot":"","sources":["../../../../src/core/progress/cancelation-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAGpD;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,0DAA0D;IAC1D,WAAW,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAClC,sBAAsB;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,2DAA2D;IAC3D,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,WAAW,CAAC,CAAsB;IAC1C,OAAO,CAAC,gBAAgB,CAAS;IAEjC,OAAO,CAAC,eAAe,CAAkB;IACzC,OAAO,CAAC,cAAc,CAAuB;IAC7C,OAAO,CAAC,aAAa,CAA6B;gBAEtC,OAAO,GAAE,kBAAuB;IAM5C;;OAEG;IACH,QAAQ,IAAI,IAAI;IAQhB;;OAEG;IACH,UAAU,IAAI,IAAI;IAOlB;;;;OAIG;IACH,YAAY,IAAI,OAAO;IAIvB;;OAEG;YACW,YAAY;IAiC1B;;OAEG;IACH,KAAK,IAAI,IAAI;CAId"}
|