specweave 0.23.8 → 0.23.12
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 +391 -1338
- 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/commands/migrate-config.d.ts +22 -0
- package/dist/src/cli/commands/migrate-config.d.ts.map +1 -0
- package/dist/src/cli/commands/migrate-config.js +149 -0
- package/dist/src/cli/commands/migrate-config.js.map +1 -0
- 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/index.d.ts.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/index.js +112 -60
- package/dist/src/cli/helpers/issue-tracker/index.js.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/jira.d.ts +26 -2
- package/dist/src/cli/helpers/issue-tracker/jira.d.ts.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/jira.js +197 -132
- 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/config/config-manager.d.ts +135 -0
- package/dist/src/core/config/config-manager.d.ts.map +1 -0
- package/dist/src/core/config/config-manager.js +341 -0
- package/dist/src/core/config/config-manager.js.map +1 -0
- package/dist/src/core/config/config-migrator.d.ts +102 -0
- package/dist/src/core/config/config-migrator.d.ts.map +1 -0
- package/dist/src/core/config/config-migrator.js +367 -0
- package/dist/src/core/config/config-migrator.js.map +1 -0
- package/dist/src/core/config/index.d.ts +10 -0
- package/dist/src/core/config/index.d.ts.map +1 -0
- package/dist/src/core/config/index.js +10 -0
- package/dist/src/core/config/index.js.map +1 -0
- package/dist/src/core/config/types.d.ts +216 -0
- package/dist/src/core/config/types.d.ts.map +1 -0
- package/dist/src/core/config/types.js +32 -0
- package/dist/src/core/config/types.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/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/dist/src/integrations/jira/jira-hierarchy-mapper.d.ts +104 -0
- package/dist/src/integrations/jira/jira-hierarchy-mapper.d.ts.map +1 -0
- package/dist/src/integrations/jira/jira-hierarchy-mapper.js +178 -0
- package/dist/src/integrations/jira/jira-hierarchy-mapper.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/commands/migrate-config.md +104 -0
- package/plugins/specweave/hooks/post-edit-spec.sh +202 -31
- package/plugins/specweave/hooks/post-task-completion.sh +225 -228
- package/plugins/specweave/hooks/post-write-spec.sh +207 -31
- package/plugins/specweave/hooks/pre-edit-spec.sh +151 -0
- package/plugins/specweave/hooks/pre-task-completion.sh +5 -7
- package/plugins/specweave/hooks/pre-write-spec.sh +151 -0
- package/plugins/specweave/hooks/test-pretooluse-env.sh +72 -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-task-completion.sh +1 -1
- package/plugins/specweave-github/hooks/post-task-completion.sh +1 -1
- 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 +1 -1
- 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 +228 -6333
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { Logger } from '../../utils/logger.js';
|
|
2
|
+
/**
|
|
3
|
+
* Cached data wrapper with TTL metadata
|
|
4
|
+
*/
|
|
5
|
+
export interface CachedData<T> {
|
|
6
|
+
data: T;
|
|
7
|
+
timestamp: number;
|
|
8
|
+
ttl: number;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Cache statistics for monitoring and debugging
|
|
12
|
+
*/
|
|
13
|
+
export interface CacheStats {
|
|
14
|
+
totalFiles: number;
|
|
15
|
+
totalSize: number;
|
|
16
|
+
oldestCache: string | null;
|
|
17
|
+
oldestCacheAge: number | null;
|
|
18
|
+
providers: {
|
|
19
|
+
[provider: string]: {
|
|
20
|
+
files: number;
|
|
21
|
+
size: number;
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* CacheManager - Manages file-based caching with TTL validation
|
|
27
|
+
*
|
|
28
|
+
* Features:
|
|
29
|
+
* - 24-hour TTL by default (configurable)
|
|
30
|
+
* - Atomic writes (temp file + rename)
|
|
31
|
+
* - Corruption detection and auto-recovery
|
|
32
|
+
* - Per-project cache separation
|
|
33
|
+
* - Thread-safe operations
|
|
34
|
+
*
|
|
35
|
+
* Cache file format:
|
|
36
|
+
* {
|
|
37
|
+
* "data": <T>,
|
|
38
|
+
* "timestamp": 1700000000000,
|
|
39
|
+
* "ttl": 86400000
|
|
40
|
+
* }
|
|
41
|
+
*/
|
|
42
|
+
export declare class CacheManager {
|
|
43
|
+
private cacheDir;
|
|
44
|
+
private logger;
|
|
45
|
+
private defaultTTL;
|
|
46
|
+
constructor(projectRoot: string, options?: {
|
|
47
|
+
logger?: Logger;
|
|
48
|
+
ttl?: number;
|
|
49
|
+
});
|
|
50
|
+
/**
|
|
51
|
+
* Get cached data if valid (within TTL)
|
|
52
|
+
*
|
|
53
|
+
* @param key Cache key (e.g., "jira-projects", "jira-BACKEND-deps")
|
|
54
|
+
* @returns Cached data or null if cache miss/expired/corrupted
|
|
55
|
+
*/
|
|
56
|
+
get<T>(key: string): Promise<T | null>;
|
|
57
|
+
/**
|
|
58
|
+
* Get cached data even if expired (stale cache fallback)
|
|
59
|
+
*
|
|
60
|
+
* Used for rate limit fallback: when API rate limit hit,
|
|
61
|
+
* use stale cache instead of failing
|
|
62
|
+
*
|
|
63
|
+
* @param key Cache key
|
|
64
|
+
* @returns Cached data or null if missing/corrupted (ignores TTL)
|
|
65
|
+
*/
|
|
66
|
+
getStale<T>(key: string): Promise<T | null>;
|
|
67
|
+
/**
|
|
68
|
+
* Set cached data with current timestamp and TTL
|
|
69
|
+
*
|
|
70
|
+
* Uses atomic write pattern (temp file + rename) to prevent corruption
|
|
71
|
+
*
|
|
72
|
+
* @param key Cache key
|
|
73
|
+
* @param data Data to cache
|
|
74
|
+
* @param ttl Optional custom TTL (overrides default)
|
|
75
|
+
*/
|
|
76
|
+
set<T>(key: string, data: T, ttl?: number): Promise<void>;
|
|
77
|
+
/**
|
|
78
|
+
* Delete cached data
|
|
79
|
+
*
|
|
80
|
+
* @param key Cache key
|
|
81
|
+
*/
|
|
82
|
+
delete(key: string): Promise<void>;
|
|
83
|
+
/**
|
|
84
|
+
* Clear all cached data
|
|
85
|
+
*/
|
|
86
|
+
clearAll(): Promise<void>;
|
|
87
|
+
/**
|
|
88
|
+
* Get cache statistics for monitoring
|
|
89
|
+
*/
|
|
90
|
+
getStats(): Promise<CacheStats>;
|
|
91
|
+
/**
|
|
92
|
+
* Delete caches older than specified age
|
|
93
|
+
*
|
|
94
|
+
* @param maxAgeMs Maximum age in milliseconds
|
|
95
|
+
* @returns Count of deleted caches
|
|
96
|
+
*/
|
|
97
|
+
deleteOlderThan(maxAgeMs: number): Promise<number>;
|
|
98
|
+
/**
|
|
99
|
+
* Check if cached data is still valid (within TTL)
|
|
100
|
+
*/
|
|
101
|
+
private isValid;
|
|
102
|
+
/**
|
|
103
|
+
* Validate cache structure has required fields
|
|
104
|
+
*/
|
|
105
|
+
private isValidCacheStructure;
|
|
106
|
+
/**
|
|
107
|
+
* Get full cache file path for a key
|
|
108
|
+
*/
|
|
109
|
+
private getCachePath;
|
|
110
|
+
/**
|
|
111
|
+
* Ensure cache directory exists
|
|
112
|
+
*/
|
|
113
|
+
private ensureCacheDir;
|
|
114
|
+
/**
|
|
115
|
+
* Log cache errors to dedicated error log
|
|
116
|
+
*/
|
|
117
|
+
private logCacheError;
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=cache-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache-manager.d.ts","sourceRoot":"","sources":["../../../../src/core/cache/cache-manager.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,MAAM,EAAiB,MAAM,uBAAuB,CAAC;AAE9D;;GAEG;AACH,MAAM,WAAW,UAAU,CAAC,CAAC;IAC3B,IAAI,EAAE,CAAC,CAAC;IACR,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,SAAS,EAAE;QACT,CAAC,QAAQ,EAAE,MAAM,GAAG;YAClB,KAAK,EAAE,MAAM,CAAC;YACd,IAAI,EAAE,MAAM,CAAC;SACd,CAAC;KACH,CAAC;CACH;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,UAAU,CAA+B;gBAG/C,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE;QACP,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,GAAG,CAAC,EAAE,MAAM,CAAC;KACT;IASR;;;;;OAKG;IACG,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;IA0C5C;;;;;;;;OAQG;IACG,QAAQ,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;IAkCjD;;;;;;;;OAQG;IACG,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAgC/D;;;;OAIG;IACG,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IASxC;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAgB/B;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,UAAU,CAAC;IAwDrC;;;;;OAKG;IACG,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAkCxD;;OAEG;IACH,OAAO,CAAC,OAAO;IAMf;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAY7B;;OAEG;IACH,OAAO,CAAC,YAAY;IAIpB;;OAEG;YACW,cAAc;IAM5B;;OAEG;YACW,aAAa;CAa5B"}
|
|
@@ -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"}
|