specweave 0.17.16 → 0.17.19
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.md +405 -2495
- package/README.md +92 -2
- package/dist/locales/de/.gitkeep +0 -0
- package/dist/locales/de/cli.json +108 -0
- package/dist/locales/en/cli.json +287 -0
- package/dist/locales/en/errors.json +7 -0
- package/dist/locales/en/templates.json +6 -0
- package/dist/locales/es/.gitkeep +0 -0
- package/dist/locales/es/cli.json +41 -0
- package/dist/locales/fr/.gitkeep +0 -0
- package/dist/locales/fr/cli.json +108 -0
- package/dist/locales/ja/.gitkeep +0 -0
- package/dist/locales/ja/cli.json +108 -0
- package/dist/locales/ko/.gitkeep +0 -0
- package/dist/locales/ko/cli.json +108 -0
- package/dist/locales/pt/.gitkeep +0 -0
- package/dist/locales/pt/cli.json +108 -0
- package/dist/locales/ru/.gitkeep +0 -0
- package/dist/locales/ru/cli.json +269 -0
- package/dist/locales/zh/.gitkeep +0 -0
- package/dist/locales/zh/cli.json +108 -0
- package/dist/plugins/specweave/lib/hooks/sync-living-docs.d.ts.map +1 -1
- package/dist/plugins/specweave/lib/hooks/sync-living-docs.js +188 -36
- package/dist/plugins/specweave/lib/hooks/sync-living-docs.js.map +1 -1
- package/dist/plugins/specweave-ado/lib/ado-spec-content-sync.d.ts.map +1 -1
- package/dist/plugins/specweave-ado/lib/ado-spec-content-sync.js +65 -6
- package/dist/plugins/specweave-ado/lib/ado-spec-content-sync.js.map +1 -1
- package/dist/plugins/specweave-ado/lib/ado-status-sync.d.ts +54 -0
- package/dist/plugins/specweave-ado/lib/ado-status-sync.d.ts.map +1 -0
- package/dist/plugins/specweave-ado/lib/ado-status-sync.js +86 -0
- package/dist/plugins/specweave-ado/lib/ado-status-sync.js.map +1 -0
- package/dist/plugins/specweave-ado/lib/enhanced-ado-sync.d.ts +25 -0
- package/dist/plugins/specweave-ado/lib/enhanced-ado-sync.d.ts.map +1 -0
- package/dist/plugins/specweave-ado/lib/enhanced-ado-sync.js +191 -0
- package/dist/plugins/specweave-ado/lib/enhanced-ado-sync.js.map +1 -0
- package/dist/plugins/specweave-github/lib/duplicate-detector.d.ts +139 -0
- package/dist/plugins/specweave-github/lib/duplicate-detector.d.ts.map +1 -0
- package/dist/plugins/specweave-github/lib/duplicate-detector.js +389 -0
- package/dist/plugins/specweave-github/lib/duplicate-detector.js.map +1 -0
- package/dist/plugins/specweave-github/lib/enhanced-github-sync.d.ts +26 -0
- package/dist/plugins/specweave-github/lib/enhanced-github-sync.d.ts.map +1 -0
- package/dist/plugins/specweave-github/lib/enhanced-github-sync.js +249 -0
- package/dist/plugins/specweave-github/lib/enhanced-github-sync.js.map +1 -0
- package/dist/plugins/specweave-github/lib/epic-content-builder.d.ts +63 -0
- package/dist/plugins/specweave-github/lib/epic-content-builder.d.ts.map +1 -0
- package/dist/plugins/specweave-github/lib/epic-content-builder.js +216 -0
- package/dist/plugins/specweave-github/lib/epic-content-builder.js.map +1 -0
- package/dist/plugins/specweave-github/lib/github-client.d.ts +1 -1
- package/dist/plugins/specweave-github/lib/github-client.d.ts.map +1 -1
- package/dist/plugins/specweave-github/lib/github-client.js +25 -13
- package/dist/plugins/specweave-github/lib/github-client.js.map +1 -1
- package/dist/plugins/specweave-github/lib/github-epic-sync.d.ts +83 -0
- package/dist/plugins/specweave-github/lib/github-epic-sync.d.ts.map +1 -0
- package/dist/plugins/specweave-github/lib/github-epic-sync.js +466 -0
- package/dist/plugins/specweave-github/lib/github-epic-sync.js.map +1 -0
- package/dist/plugins/specweave-github/lib/github-status-sync.d.ts +43 -0
- package/dist/plugins/specweave-github/lib/github-status-sync.d.ts.map +1 -0
- package/dist/plugins/specweave-github/lib/github-status-sync.js +82 -0
- package/dist/plugins/specweave-github/lib/github-status-sync.js.map +1 -0
- package/dist/plugins/specweave-github/lib/task-sync.d.ts +5 -0
- package/dist/plugins/specweave-github/lib/task-sync.d.ts.map +1 -1
- package/dist/plugins/specweave-github/lib/task-sync.js +38 -2
- package/dist/plugins/specweave-github/lib/task-sync.js.map +1 -1
- package/dist/plugins/specweave-jira/lib/enhanced-jira-sync.d.ts +28 -0
- package/dist/plugins/specweave-jira/lib/enhanced-jira-sync.d.ts.map +1 -0
- package/dist/plugins/specweave-jira/lib/enhanced-jira-sync.js +156 -0
- package/dist/plugins/specweave-jira/lib/enhanced-jira-sync.js.map +1 -0
- package/dist/plugins/specweave-jira/lib/jira-epic-sync.d.ts +66 -0
- package/dist/plugins/specweave-jira/lib/jira-epic-sync.d.ts.map +1 -0
- package/dist/plugins/specweave-jira/lib/jira-epic-sync.js +274 -0
- package/dist/plugins/specweave-jira/lib/jira-epic-sync.js.map +1 -0
- package/dist/plugins/specweave-jira/lib/jira-status-sync.d.ts +56 -0
- package/dist/plugins/specweave-jira/lib/jira-status-sync.d.ts.map +1 -0
- package/dist/plugins/specweave-jira/lib/jira-status-sync.js +93 -0
- package/dist/plugins/specweave-jira/lib/jira-status-sync.js.map +1 -0
- package/dist/spec-parser.js +629 -0
- package/dist/src/cli/commands/init.d.ts.map +1 -1
- package/dist/src/cli/commands/init.js +107 -3
- package/dist/src/cli/commands/init.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 +48 -3
- package/dist/src/cli/helpers/issue-tracker/index.js.map +1 -1
- package/dist/src/core/deduplication/command-deduplicator.d.ts +166 -0
- package/dist/src/core/deduplication/command-deduplicator.d.ts.map +1 -0
- package/dist/src/core/deduplication/command-deduplicator.js +254 -0
- package/dist/src/core/deduplication/command-deduplicator.js.map +1 -0
- package/dist/src/core/living-docs/hierarchy-mapper.d.ts +142 -0
- package/dist/src/core/living-docs/hierarchy-mapper.d.ts.map +1 -0
- package/dist/src/core/living-docs/hierarchy-mapper.js +453 -0
- package/dist/src/core/living-docs/hierarchy-mapper.js.map +1 -0
- package/dist/src/core/living-docs/index.d.ts +10 -84
- package/dist/src/core/living-docs/index.d.ts.map +1 -1
- package/dist/src/core/living-docs/index.js +10 -164
- package/dist/src/core/living-docs/index.js.map +1 -1
- package/dist/src/core/living-docs/spec-distributor.d.ts +106 -0
- package/dist/src/core/living-docs/spec-distributor.d.ts.map +1 -0
- package/dist/src/core/living-docs/spec-distributor.js +823 -0
- package/dist/src/core/living-docs/spec-distributor.js.map +1 -0
- package/dist/src/core/living-docs/types.d.ts +201 -0
- package/dist/src/core/living-docs/types.d.ts.map +1 -0
- package/dist/src/core/living-docs/types.js +15 -0
- package/dist/src/core/living-docs/types.js.map +1 -0
- package/dist/src/core/logging/prompt-logger.d.ts +70 -0
- package/dist/src/core/logging/prompt-logger.d.ts.map +1 -0
- package/dist/src/core/logging/prompt-logger.js +247 -0
- package/dist/src/core/logging/prompt-logger.js.map +1 -0
- package/dist/src/core/status-line/status-line-manager.d.ts +15 -24
- package/dist/src/core/status-line/status-line-manager.d.ts.map +1 -1
- package/dist/src/core/status-line/status-line-manager.js +33 -70
- package/dist/src/core/status-line/status-line-manager.js.map +1 -1
- package/dist/src/core/status-line/types.d.ts +19 -31
- package/dist/src/core/status-line/types.d.ts.map +1 -1
- package/dist/src/core/status-line/types.js +5 -9
- package/dist/src/core/status-line/types.js.map +1 -1
- package/dist/src/core/sync/conflict-resolver.d.ts +66 -0
- package/dist/src/core/sync/conflict-resolver.d.ts.map +1 -0
- package/dist/src/core/sync/conflict-resolver.js +108 -0
- package/dist/src/core/sync/conflict-resolver.js.map +1 -0
- package/dist/src/core/sync/enhanced-content-builder.d.ts +55 -0
- package/dist/src/core/sync/enhanced-content-builder.d.ts.map +1 -0
- package/dist/src/core/sync/enhanced-content-builder.js +202 -0
- package/dist/src/core/sync/enhanced-content-builder.js.map +1 -0
- package/dist/src/core/sync/label-detector.d.ts +66 -0
- package/dist/src/core/sync/label-detector.d.ts.map +1 -0
- package/dist/src/core/sync/label-detector.js +211 -0
- package/dist/src/core/sync/label-detector.js.map +1 -0
- package/dist/src/core/sync/retry-logic.d.ts +64 -0
- package/dist/src/core/sync/retry-logic.d.ts.map +1 -0
- package/dist/src/core/sync/retry-logic.js +165 -0
- package/dist/src/core/sync/retry-logic.js.map +1 -0
- package/dist/src/core/sync/spec-content-sync.d.ts +88 -0
- package/dist/src/core/sync/spec-content-sync.d.ts.map +1 -0
- package/dist/src/core/sync/spec-content-sync.js +5 -0
- package/dist/src/core/sync/spec-content-sync.js.map +1 -0
- package/dist/src/core/sync/spec-increment-mapper.d.ts +100 -0
- package/dist/src/core/sync/spec-increment-mapper.d.ts.map +1 -0
- package/dist/src/core/sync/spec-increment-mapper.js +424 -0
- package/dist/src/core/sync/spec-increment-mapper.js.map +1 -0
- package/dist/src/core/sync/status-cache.d.ts +91 -0
- package/dist/src/core/sync/status-cache.d.ts.map +1 -0
- package/dist/src/core/sync/status-cache.js +140 -0
- package/dist/src/core/sync/status-cache.js.map +1 -0
- package/dist/src/core/sync/status-mapper.d.ts +69 -0
- package/dist/src/core/sync/status-mapper.d.ts.map +1 -0
- package/dist/src/core/sync/status-mapper.js +90 -0
- package/dist/src/core/sync/status-mapper.js.map +1 -0
- package/dist/src/core/sync/status-sync-engine.d.ts +162 -0
- package/dist/src/core/sync/status-sync-engine.d.ts.map +1 -0
- package/dist/src/core/sync/status-sync-engine.js +347 -0
- package/dist/src/core/sync/status-sync-engine.js.map +1 -0
- package/dist/src/core/sync/sync-event-logger.d.ts +99 -0
- package/dist/src/core/sync/sync-event-logger.d.ts.map +1 -0
- package/dist/src/core/sync/sync-event-logger.js +103 -0
- package/dist/src/core/sync/sync-event-logger.js.map +1 -0
- package/dist/src/core/sync/types.d.ts +52 -0
- package/dist/src/core/sync/types.d.ts.map +1 -0
- package/dist/src/core/sync/types.js +5 -0
- package/dist/src/core/sync/types.js.map +1 -0
- package/dist/src/core/sync/workflow-detector.d.ts +95 -0
- package/dist/src/core/sync/workflow-detector.d.ts.map +1 -0
- package/dist/src/core/sync/workflow-detector.js +175 -0
- package/dist/src/core/sync/workflow-detector.js.map +1 -0
- package/dist/src/core/types/config.d.ts +51 -0
- package/dist/src/core/types/config.d.ts.map +1 -1
- package/dist/src/core/types/config.js +47 -0
- package/dist/src/core/types/config.js.map +1 -1
- package/dist/src/core/types/increment-metadata.d.ts +4 -0
- package/dist/src/core/types/increment-metadata.d.ts.map +1 -1
- package/dist/src/core/types/increment-metadata.js.map +1 -1
- package/dist/src/utils/github-url.d.ts +53 -0
- package/dist/src/utils/github-url.d.ts.map +1 -0
- package/dist/src/utils/github-url.js +90 -0
- package/dist/src/utils/github-url.js.map +1 -0
- package/dist/src/utils/spec-parser.d.ts +145 -0
- package/dist/src/utils/spec-parser.d.ts.map +1 -0
- package/dist/src/utils/spec-parser.js +640 -0
- package/dist/src/utils/spec-parser.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +1 -1
- package/plugins/specweave/agents/pm/AGENT.md +160 -13
- package/plugins/specweave/agents/pm/templates/increment-spec.md +158 -0
- package/plugins/specweave/agents/pm/templates/living-docs-spec.md +113 -0
- package/plugins/specweave/commands/specweave-done.md +163 -0
- package/plugins/specweave/commands/specweave.md +70 -405
- package/plugins/specweave/hooks/hooks.json +4 -0
- package/plugins/specweave/hooks/lib/sync-spec-content.sh +2 -2
- package/plugins/specweave/hooks/lib/update-status-line.sh +79 -111
- package/plugins/specweave/hooks/post-increment-planning.sh +133 -37
- package/plugins/specweave/hooks/pre-command-deduplication.sh +86 -0
- package/plugins/specweave/lib/hooks/sync-living-docs.js +139 -34
- package/plugins/specweave/lib/hooks/sync-living-docs.ts +234 -38
- package/plugins/specweave/skills/SKILLS-INDEX.md +4 -24
- package/plugins/specweave/skills/increment-planner/SKILL.md +94 -0
- package/plugins/specweave/skills/increment-work-router/SKILL.md +466 -0
- package/plugins/specweave-ado/commands/specweave-ado-sync-spec.md +1 -1
- package/plugins/specweave-ado/lib/ado-spec-content-sync.js +49 -5
- package/plugins/specweave-ado/lib/ado-spec-content-sync.ts +72 -6
- package/plugins/specweave-ado/lib/ado-status-sync.js +80 -0
- package/plugins/specweave-ado/lib/ado-status-sync.ts +121 -0
- package/plugins/specweave-ado/lib/enhanced-ado-sync.js +170 -0
- package/plugins/specweave-github/commands/specweave-github-cleanup-duplicates.md +205 -0
- package/plugins/specweave-github/commands/specweave-github-sync-epic.md +248 -0
- package/plugins/specweave-github/commands/specweave-github-sync-spec.md +1 -1
- package/plugins/specweave-github/hooks/post-task-completion.sh +32 -0
- package/plugins/specweave-github/lib/duplicate-detector.js +370 -0
- package/plugins/specweave-github/lib/duplicate-detector.ts +525 -0
- package/plugins/specweave-github/lib/enhanced-github-sync.js +220 -0
- package/plugins/specweave-github/lib/enhanced-github-sync.ts +322 -0
- package/plugins/specweave-github/lib/epic-content-builder.js +227 -0
- package/plugins/specweave-github/lib/epic-content-builder.ts +317 -0
- package/plugins/specweave-github/lib/github-client.js +21 -10
- package/plugins/specweave-github/lib/github-client.ts +27 -16
- package/plugins/specweave-github/lib/github-epic-sync.js +488 -0
- package/plugins/specweave-github/lib/github-epic-sync.ts +715 -0
- package/plugins/specweave-github/lib/github-status-sync.js +71 -0
- package/plugins/specweave-github/lib/github-status-sync.ts +107 -0
- package/plugins/specweave-github/lib/task-sync.js +33 -2
- package/plugins/specweave-github/lib/task-sync.ts +44 -2
- package/plugins/specweave-jira/commands/specweave-jira-sync-epic.md +267 -0
- package/plugins/specweave-jira/commands/specweave-jira-sync-spec.md +1 -1
- package/plugins/specweave-jira/lib/enhanced-jira-sync.js +134 -0
- package/plugins/specweave-jira/lib/enhanced-jira-sync.ts +196 -0
- package/plugins/specweave-jira/lib/jira-epic-sync.js +304 -0
- package/plugins/specweave-jira/lib/jira-epic-sync.ts +459 -0
- package/plugins/specweave-jira/lib/jira-status-sync.js +79 -0
- package/plugins/specweave-jira/lib/jira-status-sync.ts +139 -0
- package/plugins/specweave-release/commands/specweave-release-platform.md +1 -1
- package/plugins/specweave-release/hooks/post-task-completion.sh +2 -2
- package/src/templates/AGENTS.md.template +88 -1
- package/src/templates/CLAUDE.md.template +49 -0
- package/plugins/specweave/skills/increment-quality-judge/SKILL.md +0 -524
- package/plugins/specweave/skills/plugin-installer/SKILL.md +0 -353
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spec-to-Increment Mapper
|
|
3
|
+
*
|
|
4
|
+
* Maps permanent living docs specs to specific increment tasks.
|
|
5
|
+
* Enables traceability: "Which increment implemented US-001?"
|
|
6
|
+
*/
|
|
7
|
+
import fs from 'fs/promises';
|
|
8
|
+
import path from 'path';
|
|
9
|
+
import matter from 'gray-matter';
|
|
10
|
+
export class SpecIncrementMapper {
|
|
11
|
+
constructor(rootDir = process.cwd()) {
|
|
12
|
+
this.rootDir = rootDir;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Map a spec to all increments that implement it
|
|
16
|
+
*/
|
|
17
|
+
async mapSpecToIncrements(specId) {
|
|
18
|
+
const increments = await this.findIncrementsForSpec(specId);
|
|
19
|
+
const userStoryMappings = {};
|
|
20
|
+
// Build user story to task mappings
|
|
21
|
+
for (const increment of increments) {
|
|
22
|
+
for (const task of increment.tasks) {
|
|
23
|
+
for (const userStoryId of task.userStories) {
|
|
24
|
+
if (!userStoryMappings[userStoryId]) {
|
|
25
|
+
userStoryMappings[userStoryId] = [];
|
|
26
|
+
}
|
|
27
|
+
userStoryMappings[userStoryId].push(task);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
specId,
|
|
33
|
+
increments,
|
|
34
|
+
userStoryMappings
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Find all increments that implement a specific user story
|
|
39
|
+
*/
|
|
40
|
+
async findIncrementsByUserStory(userStoryId) {
|
|
41
|
+
const incrementsDir = path.join(this.rootDir, '.specweave/increments');
|
|
42
|
+
try {
|
|
43
|
+
const entries = await fs.readdir(incrementsDir, { withFileTypes: true });
|
|
44
|
+
const incrementDirs = entries
|
|
45
|
+
.filter(e => e.isDirectory() && /^\d{4}-/.test(e.name))
|
|
46
|
+
.map(e => e.name);
|
|
47
|
+
const results = [];
|
|
48
|
+
for (const incrementId of incrementDirs) {
|
|
49
|
+
const tasks = await this.getTasksFromIncrement(incrementId);
|
|
50
|
+
const relevantTasks = tasks.filter(t => t.userStories.includes(userStoryId));
|
|
51
|
+
if (relevantTasks.length > 0) {
|
|
52
|
+
results.push({
|
|
53
|
+
id: incrementId,
|
|
54
|
+
tasks: relevantTasks
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return results;
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
return [];
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Get all tasks that implement a specific user story
|
|
66
|
+
*/
|
|
67
|
+
async getTasksForUserStory(userStoryId) {
|
|
68
|
+
const increments = await this.findIncrementsByUserStory(userStoryId);
|
|
69
|
+
const tasks = [];
|
|
70
|
+
for (const increment of increments) {
|
|
71
|
+
tasks.push(...increment.tasks);
|
|
72
|
+
}
|
|
73
|
+
return tasks;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Update spec with increment links in frontmatter (Forward link)
|
|
77
|
+
*/
|
|
78
|
+
async updateSpecWithIncrementLinks(specId, incrementId) {
|
|
79
|
+
const specPath = await this.findSpecPath(specId);
|
|
80
|
+
if (!specPath) {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
try {
|
|
84
|
+
const content = await fs.readFile(specPath, 'utf-8');
|
|
85
|
+
const { data: frontmatter, content: markdownContent } = matter(content);
|
|
86
|
+
// Initialize linked_increments if not exists
|
|
87
|
+
if (!frontmatter.linked_increments) {
|
|
88
|
+
frontmatter.linked_increments = [];
|
|
89
|
+
}
|
|
90
|
+
// Check if already linked
|
|
91
|
+
if (frontmatter.linked_increments.includes(incrementId)) {
|
|
92
|
+
return false; // Already linked
|
|
93
|
+
}
|
|
94
|
+
// Add new increment link
|
|
95
|
+
frontmatter.linked_increments.push(incrementId);
|
|
96
|
+
// Write back
|
|
97
|
+
const updated = matter.stringify(markdownContent, frontmatter);
|
|
98
|
+
await fs.writeFile(specPath, updated);
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Update increment with spec link in frontmatter (Backward link)
|
|
107
|
+
*/
|
|
108
|
+
async updateIncrementWithSpecLink(incrementId, specId) {
|
|
109
|
+
const incrementSpecPath = path.join(this.rootDir, '.specweave/increments', incrementId, 'spec.md');
|
|
110
|
+
try {
|
|
111
|
+
await fs.access(incrementSpecPath);
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
return false; // Increment spec.md doesn't exist
|
|
115
|
+
}
|
|
116
|
+
try {
|
|
117
|
+
const content = await fs.readFile(incrementSpecPath, 'utf-8');
|
|
118
|
+
const { data: frontmatter, content: markdownContent } = matter(content);
|
|
119
|
+
// Check if already linked
|
|
120
|
+
if (frontmatter.spec_id === specId || frontmatter.source_spec === specId) {
|
|
121
|
+
return false; // Already linked
|
|
122
|
+
}
|
|
123
|
+
// Add spec link
|
|
124
|
+
frontmatter.source_spec = specId;
|
|
125
|
+
// Write back
|
|
126
|
+
const updated = matter.stringify(markdownContent, frontmatter);
|
|
127
|
+
await fs.writeFile(incrementSpecPath, updated);
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Create bidirectional link between spec and increment (atomic operation)
|
|
136
|
+
*/
|
|
137
|
+
async createBidirectionalLink(specId, incrementId) {
|
|
138
|
+
const forwardLink = await this.updateSpecWithIncrementLinks(specId, incrementId);
|
|
139
|
+
const backwardLink = await this.updateIncrementWithSpecLink(incrementId, specId);
|
|
140
|
+
return {
|
|
141
|
+
success: forwardLink || backwardLink,
|
|
142
|
+
forwardLink,
|
|
143
|
+
backwardLink
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Get spec for a given increment (reverse lookup)
|
|
148
|
+
*/
|
|
149
|
+
async getSpecForIncrement(incrementId) {
|
|
150
|
+
const incrementSpecPath = path.join(this.rootDir, '.specweave/increments', incrementId, 'spec.md');
|
|
151
|
+
try {
|
|
152
|
+
const content = await fs.readFile(incrementSpecPath, 'utf-8');
|
|
153
|
+
const { data: frontmatter } = matter(content);
|
|
154
|
+
// Check both field names for compatibility
|
|
155
|
+
return frontmatter.source_spec || frontmatter.spec_id || null;
|
|
156
|
+
}
|
|
157
|
+
catch {
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Validate bidirectional links and detect broken references
|
|
163
|
+
*/
|
|
164
|
+
async validateLinks() {
|
|
165
|
+
const brokenForwardLinks = [];
|
|
166
|
+
const brokenBackwardLinks = [];
|
|
167
|
+
const orphanedIncrements = [];
|
|
168
|
+
const incrementsDir = path.join(this.rootDir, '.specweave/increments');
|
|
169
|
+
try {
|
|
170
|
+
const entries = await fs.readdir(incrementsDir, { withFileTypes: true });
|
|
171
|
+
const incrementDirs = entries
|
|
172
|
+
.filter(e => e.isDirectory() && /^\d{4}-/.test(e.name))
|
|
173
|
+
.map(e => e.name);
|
|
174
|
+
for (const incrementId of incrementDirs) {
|
|
175
|
+
// Check backward link
|
|
176
|
+
const specId = await this.getSpecForIncrement(incrementId);
|
|
177
|
+
if (!specId) {
|
|
178
|
+
orphanedIncrements.push(incrementId);
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
// Validate forward link exists
|
|
182
|
+
const specPath = await this.findSpecPath(specId);
|
|
183
|
+
if (specPath) {
|
|
184
|
+
const content = await fs.readFile(specPath, 'utf-8');
|
|
185
|
+
const { data: frontmatter } = matter(content);
|
|
186
|
+
if (!frontmatter.linked_increments?.includes(incrementId)) {
|
|
187
|
+
brokenBackwardLinks.push({ incrementId, specId });
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
brokenBackwardLinks.push({ incrementId, specId });
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
// Check specs for broken forward links
|
|
195
|
+
const specsDir = path.join(this.rootDir, '.specweave/docs/internal/specs');
|
|
196
|
+
const projects = await fs.readdir(specsDir, { withFileTypes: true });
|
|
197
|
+
for (const project of projects.filter(e => e.isDirectory())) {
|
|
198
|
+
const specs = await fs.readdir(path.join(specsDir, project.name));
|
|
199
|
+
for (const specFile of specs.filter(f => f.endsWith('.md'))) {
|
|
200
|
+
const specPath = path.join(specsDir, project.name, specFile);
|
|
201
|
+
const content = await fs.readFile(specPath, 'utf-8');
|
|
202
|
+
const { data: frontmatter } = matter(content);
|
|
203
|
+
if (frontmatter.linked_increments) {
|
|
204
|
+
for (const incrementId of frontmatter.linked_increments) {
|
|
205
|
+
const incrementPath = path.join(incrementsDir, incrementId, 'spec.md');
|
|
206
|
+
try {
|
|
207
|
+
await fs.access(incrementPath);
|
|
208
|
+
}
|
|
209
|
+
catch {
|
|
210
|
+
brokenForwardLinks.push({
|
|
211
|
+
specId: specFile.replace('.md', ''),
|
|
212
|
+
incrementId
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
catch (error) {
|
|
221
|
+
// Directory doesn't exist or other error
|
|
222
|
+
}
|
|
223
|
+
return {
|
|
224
|
+
valid: brokenForwardLinks.length === 0 &&
|
|
225
|
+
brokenBackwardLinks.length === 0 &&
|
|
226
|
+
orphanedIncrements.length === 0,
|
|
227
|
+
brokenForwardLinks,
|
|
228
|
+
brokenBackwardLinks,
|
|
229
|
+
orphanedIncrements
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Build complete traceability report for a spec
|
|
234
|
+
*/
|
|
235
|
+
async buildTraceabilityReport(specId) {
|
|
236
|
+
const mapping = await this.mapSpecToIncrements(specId);
|
|
237
|
+
const userStories = await this.getUserStoriesFromSpec(specId);
|
|
238
|
+
const totalUserStories = userStories.length;
|
|
239
|
+
const totalIncrements = mapping.increments.length;
|
|
240
|
+
const totalTasks = mapping.increments.reduce((sum, inc) => sum + inc.tasks.length, 0);
|
|
241
|
+
const mappedUserStories = Object.keys(mapping.userStoryMappings).filter(us => userStories.includes(us));
|
|
242
|
+
const unmappedUserStories = userStories.filter(us => !mappedUserStories.includes(us));
|
|
243
|
+
const coverage = totalUserStories > 0
|
|
244
|
+
? Math.min(100, (mappedUserStories.length / totalUserStories) * 100)
|
|
245
|
+
: 0;
|
|
246
|
+
// Build detailed mapping
|
|
247
|
+
const mappingDetails = {};
|
|
248
|
+
for (const [userStoryId, tasks] of Object.entries(mapping.userStoryMappings)) {
|
|
249
|
+
const incrementGroups = {};
|
|
250
|
+
for (const task of tasks) {
|
|
251
|
+
if (!incrementGroups[task.incrementId]) {
|
|
252
|
+
incrementGroups[task.incrementId] = [];
|
|
253
|
+
}
|
|
254
|
+
incrementGroups[task.incrementId].push(task.id);
|
|
255
|
+
}
|
|
256
|
+
mappingDetails[userStoryId] = Object.entries(incrementGroups).map(([incrementId, taskIds]) => ({
|
|
257
|
+
incrementId,
|
|
258
|
+
taskIds
|
|
259
|
+
}));
|
|
260
|
+
}
|
|
261
|
+
return {
|
|
262
|
+
specId,
|
|
263
|
+
totalUserStories,
|
|
264
|
+
totalIncrements,
|
|
265
|
+
totalTasks,
|
|
266
|
+
coverage,
|
|
267
|
+
unmappedUserStories,
|
|
268
|
+
mappingDetails
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
// Private helper methods
|
|
272
|
+
async findIncrementsForSpec(specId) {
|
|
273
|
+
const incrementsDir = path.join(this.rootDir, '.specweave/increments');
|
|
274
|
+
try {
|
|
275
|
+
const entries = await fs.readdir(incrementsDir, { withFileTypes: true });
|
|
276
|
+
const incrementDirs = entries
|
|
277
|
+
.filter(e => e.isDirectory() && /^\d{4}-/.test(e.name))
|
|
278
|
+
.map(e => e.name);
|
|
279
|
+
const results = [];
|
|
280
|
+
for (const incrementId of incrementDirs) {
|
|
281
|
+
const specPath = path.join(incrementsDir, incrementId, 'spec.md');
|
|
282
|
+
try {
|
|
283
|
+
const content = await fs.readFile(specPath, 'utf-8');
|
|
284
|
+
const { data: frontmatter } = matter(content);
|
|
285
|
+
// Check if this increment references our spec
|
|
286
|
+
if (frontmatter.spec_id === specId || content.includes(`spec-${specId}`)) {
|
|
287
|
+
const tasks = await this.getTasksFromIncrement(incrementId);
|
|
288
|
+
results.push({
|
|
289
|
+
id: incrementId,
|
|
290
|
+
tasks,
|
|
291
|
+
specId
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
catch {
|
|
296
|
+
// Skip if spec.md doesn't exist
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
return results;
|
|
300
|
+
}
|
|
301
|
+
catch (error) {
|
|
302
|
+
return [];
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
async getTasksFromIncrement(incrementId) {
|
|
306
|
+
const tasksPath = path.join(this.rootDir, '.specweave/increments', incrementId, 'tasks.md');
|
|
307
|
+
try {
|
|
308
|
+
const content = await fs.readFile(tasksPath, 'utf-8');
|
|
309
|
+
const { data: frontmatter } = matter(content);
|
|
310
|
+
const tasks = [];
|
|
311
|
+
// Parse tasks from markdown (tasks use ### heading level)
|
|
312
|
+
const taskRegex = /^###\s+(T-\d+):\s+(.+)$/gm;
|
|
313
|
+
let match;
|
|
314
|
+
while ((match = taskRegex.exec(content)) !== null) {
|
|
315
|
+
const taskId = match[1];
|
|
316
|
+
const title = match[2];
|
|
317
|
+
// Extract user stories for this task
|
|
318
|
+
const taskSection = this.extractTaskSection(content, taskId);
|
|
319
|
+
const userStories = this.extractUserStoriesFromTask(taskSection);
|
|
320
|
+
const githubIssue = this.extractGithubIssue(taskSection);
|
|
321
|
+
const status = this.extractTaskStatus(taskSection);
|
|
322
|
+
tasks.push({
|
|
323
|
+
id: taskId,
|
|
324
|
+
title,
|
|
325
|
+
incrementId,
|
|
326
|
+
userStories,
|
|
327
|
+
githubIssue,
|
|
328
|
+
status
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
return tasks;
|
|
332
|
+
}
|
|
333
|
+
catch (error) {
|
|
334
|
+
return [];
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
extractTaskSection(content, taskId) {
|
|
338
|
+
const startRegex = new RegExp(`^###\\s+${taskId}:`, 'm');
|
|
339
|
+
const startMatch = content.match(startRegex);
|
|
340
|
+
if (!startMatch)
|
|
341
|
+
return '';
|
|
342
|
+
const startIndex = startMatch.index;
|
|
343
|
+
const nextTaskMatch = content.slice(startIndex + 1).match(/^###\s+T-\d+:/m);
|
|
344
|
+
const endIndex = nextTaskMatch
|
|
345
|
+
? startIndex + 1 + nextTaskMatch.index
|
|
346
|
+
: content.length;
|
|
347
|
+
return content.slice(startIndex, endIndex);
|
|
348
|
+
}
|
|
349
|
+
extractUserStoriesFromTask(taskSection) {
|
|
350
|
+
// Look for AC references: AC-US1-01, AC-US2-01, etc.
|
|
351
|
+
const acRegex = /AC-US(\d+)-\d+/g;
|
|
352
|
+
const matches = [...taskSection.matchAll(acRegex)];
|
|
353
|
+
const userStoryNumbers = new Set(matches.map(m => m[1]));
|
|
354
|
+
// Also look for explicit "Implements: US-001, US-002"
|
|
355
|
+
const implementsMatch = taskSection.match(/Implements:\s*([^\n]+)/);
|
|
356
|
+
if (implementsMatch) {
|
|
357
|
+
const usMatches = implementsMatch[1].matchAll(/US-(\d+)/g);
|
|
358
|
+
for (const match of usMatches) {
|
|
359
|
+
userStoryNumbers.add(match[1]);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
return Array.from(userStoryNumbers).map(num => `US-${num}`);
|
|
363
|
+
}
|
|
364
|
+
extractGithubIssue(taskSection) {
|
|
365
|
+
const match = taskSection.match(/github.*#(\d+)/i);
|
|
366
|
+
return match ? parseInt(match[1], 10) : undefined;
|
|
367
|
+
}
|
|
368
|
+
extractTaskStatus(taskSection) {
|
|
369
|
+
if (taskSection.includes('[x] Completed') || taskSection.includes('✅')) {
|
|
370
|
+
return 'completed';
|
|
371
|
+
}
|
|
372
|
+
if (taskSection.includes('[ ] In Progress') || taskSection.includes('🔄')) {
|
|
373
|
+
return 'in-progress';
|
|
374
|
+
}
|
|
375
|
+
return 'pending';
|
|
376
|
+
}
|
|
377
|
+
async findSpecPath(specId) {
|
|
378
|
+
const specsDir = path.join(this.rootDir, '.specweave/docs/internal/specs');
|
|
379
|
+
try {
|
|
380
|
+
// Try default project first
|
|
381
|
+
const defaultPath = path.join(specsDir, 'default', `${specId}.md`);
|
|
382
|
+
try {
|
|
383
|
+
await fs.access(defaultPath);
|
|
384
|
+
return defaultPath;
|
|
385
|
+
}
|
|
386
|
+
catch {
|
|
387
|
+
// Not in default, search all projects
|
|
388
|
+
}
|
|
389
|
+
// Search all project folders
|
|
390
|
+
const entries = await fs.readdir(specsDir, { withFileTypes: true });
|
|
391
|
+
const projectDirs = entries.filter(e => e.isDirectory()).map(e => e.name);
|
|
392
|
+
for (const project of projectDirs) {
|
|
393
|
+
const specPath = path.join(specsDir, project, `${specId}.md`);
|
|
394
|
+
try {
|
|
395
|
+
await fs.access(specPath);
|
|
396
|
+
return specPath;
|
|
397
|
+
}
|
|
398
|
+
catch {
|
|
399
|
+
continue;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
return null;
|
|
403
|
+
}
|
|
404
|
+
catch (error) {
|
|
405
|
+
return null;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
async getUserStoriesFromSpec(specId) {
|
|
409
|
+
const specPath = await this.findSpecPath(specId);
|
|
410
|
+
if (!specPath) {
|
|
411
|
+
return [];
|
|
412
|
+
}
|
|
413
|
+
try {
|
|
414
|
+
const content = await fs.readFile(specPath, 'utf-8');
|
|
415
|
+
const userStoryRegex = /\*\*(US-\d+)\*\*:/g;
|
|
416
|
+
const matches = [...content.matchAll(userStoryRegex)];
|
|
417
|
+
return matches.map(m => m[1]);
|
|
418
|
+
}
|
|
419
|
+
catch (error) {
|
|
420
|
+
return [];
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
//# sourceMappingURL=spec-increment-mapper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spec-increment-mapper.js","sourceRoot":"","sources":["../../../../src/core/sync/spec-increment-mapper.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Status Cache
|
|
3
|
+
*
|
|
4
|
+
* Caches external tool statuses to reduce API calls.
|
|
5
|
+
* Uses in-memory cache with TTL (default: 5 minutes).
|
|
6
|
+
*
|
|
7
|
+
* Performance Optimization:
|
|
8
|
+
* - Reduces redundant API calls
|
|
9
|
+
* - Improves sync speed
|
|
10
|
+
* - Respects rate limits
|
|
11
|
+
*
|
|
12
|
+
* @module status-cache
|
|
13
|
+
*/
|
|
14
|
+
export interface CacheEntry<T> {
|
|
15
|
+
value: T;
|
|
16
|
+
timestamp: number;
|
|
17
|
+
ttl: number;
|
|
18
|
+
}
|
|
19
|
+
export interface CacheOptions {
|
|
20
|
+
ttl?: number;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Status Cache
|
|
24
|
+
*
|
|
25
|
+
* Generic cache for external tool statuses.
|
|
26
|
+
*/
|
|
27
|
+
export declare class StatusCache<T = any> {
|
|
28
|
+
private cache;
|
|
29
|
+
private defaultTtl;
|
|
30
|
+
constructor(options?: CacheOptions);
|
|
31
|
+
/**
|
|
32
|
+
* Get value from cache
|
|
33
|
+
*
|
|
34
|
+
* @param key - Cache key
|
|
35
|
+
* @returns Cached value or undefined if not found/expired
|
|
36
|
+
*/
|
|
37
|
+
get(key: string): T | undefined;
|
|
38
|
+
/**
|
|
39
|
+
* Set value in cache
|
|
40
|
+
*
|
|
41
|
+
* @param key - Cache key
|
|
42
|
+
* @param value - Value to cache
|
|
43
|
+
* @param ttl - Optional TTL override (ms)
|
|
44
|
+
*/
|
|
45
|
+
set(key: string, value: T, ttl?: number): void;
|
|
46
|
+
/**
|
|
47
|
+
* Check if key exists and is not expired
|
|
48
|
+
*
|
|
49
|
+
* @param key - Cache key
|
|
50
|
+
* @returns True if key exists and is not expired
|
|
51
|
+
*/
|
|
52
|
+
has(key: string): boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Delete key from cache
|
|
55
|
+
*
|
|
56
|
+
* @param key - Cache key
|
|
57
|
+
*/
|
|
58
|
+
delete(key: string): void;
|
|
59
|
+
/**
|
|
60
|
+
* Clear all cache entries
|
|
61
|
+
*/
|
|
62
|
+
clear(): void;
|
|
63
|
+
/**
|
|
64
|
+
* Get cache size
|
|
65
|
+
*
|
|
66
|
+
* @returns Number of entries in cache
|
|
67
|
+
*/
|
|
68
|
+
size(): number;
|
|
69
|
+
/**
|
|
70
|
+
* Clean up expired entries
|
|
71
|
+
*
|
|
72
|
+
* Removes all expired entries from cache.
|
|
73
|
+
*/
|
|
74
|
+
cleanup(): void;
|
|
75
|
+
/**
|
|
76
|
+
* Get cache statistics
|
|
77
|
+
*
|
|
78
|
+
* @returns Cache statistics (size, oldest entry age)
|
|
79
|
+
*/
|
|
80
|
+
getStats(): {
|
|
81
|
+
size: number;
|
|
82
|
+
oldestEntryAge: number | null;
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Global status cache instance
|
|
87
|
+
*
|
|
88
|
+
* Used across all sync operations for maximum efficiency.
|
|
89
|
+
*/
|
|
90
|
+
export declare const globalStatusCache: StatusCache<any>;
|
|
91
|
+
//# sourceMappingURL=status-cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status-cache.d.ts","sourceRoot":"","sources":["../../../../src/core/sync/status-cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,MAAM,WAAW,UAAU,CAAC,CAAC;IAC3B,KAAK,EAAE,CAAC,CAAC;IACT,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,YAAY;IAC3B,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;;;GAIG;AACH,qBAAa,WAAW,CAAC,CAAC,GAAG,GAAG;IAC9B,OAAO,CAAC,KAAK,CAA6B;IAC1C,OAAO,CAAC,UAAU,CAAS;gBAEf,OAAO,CAAC,EAAE,YAAY;IAKlC;;;;;OAKG;IACH,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS;IAoB/B;;;;;;OAMG;IACH,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI;IAU9C;;;;;OAKG;IACH,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAIzB;;;;OAIG;IACH,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAIzB;;OAEG;IACH,KAAK,IAAI,IAAI;IAIb;;;;OAIG;IACH,IAAI,IAAI,MAAM;IAMd;;;;OAIG;IACH,OAAO,IAAI,IAAI;IAgBf;;;;OAIG;IACH,QAAQ,IAAI;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE;CAqB5D;AAED;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,kBAAoB,CAAC"}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Status Cache
|
|
3
|
+
*
|
|
4
|
+
* Caches external tool statuses to reduce API calls.
|
|
5
|
+
* Uses in-memory cache with TTL (default: 5 minutes).
|
|
6
|
+
*
|
|
7
|
+
* Performance Optimization:
|
|
8
|
+
* - Reduces redundant API calls
|
|
9
|
+
* - Improves sync speed
|
|
10
|
+
* - Respects rate limits
|
|
11
|
+
*
|
|
12
|
+
* @module status-cache
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Status Cache
|
|
16
|
+
*
|
|
17
|
+
* Generic cache for external tool statuses.
|
|
18
|
+
*/
|
|
19
|
+
export class StatusCache {
|
|
20
|
+
constructor(options) {
|
|
21
|
+
this.cache = new Map();
|
|
22
|
+
this.defaultTtl = options?.ttl || 5 * 60 * 1000; // 5 minutes default
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Get value from cache
|
|
26
|
+
*
|
|
27
|
+
* @param key - Cache key
|
|
28
|
+
* @returns Cached value or undefined if not found/expired
|
|
29
|
+
*/
|
|
30
|
+
get(key) {
|
|
31
|
+
const entry = this.cache.get(key);
|
|
32
|
+
if (!entry) {
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
// Check if expired
|
|
36
|
+
const now = Date.now();
|
|
37
|
+
const age = now - entry.timestamp;
|
|
38
|
+
if (age > entry.ttl) {
|
|
39
|
+
// Expired - remove and return undefined
|
|
40
|
+
this.cache.delete(key);
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
return entry.value;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Set value in cache
|
|
47
|
+
*
|
|
48
|
+
* @param key - Cache key
|
|
49
|
+
* @param value - Value to cache
|
|
50
|
+
* @param ttl - Optional TTL override (ms)
|
|
51
|
+
*/
|
|
52
|
+
set(key, value, ttl) {
|
|
53
|
+
const entry = {
|
|
54
|
+
value,
|
|
55
|
+
timestamp: Date.now(),
|
|
56
|
+
ttl: ttl || this.defaultTtl
|
|
57
|
+
};
|
|
58
|
+
this.cache.set(key, entry);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Check if key exists and is not expired
|
|
62
|
+
*
|
|
63
|
+
* @param key - Cache key
|
|
64
|
+
* @returns True if key exists and is not expired
|
|
65
|
+
*/
|
|
66
|
+
has(key) {
|
|
67
|
+
return this.get(key) !== undefined;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Delete key from cache
|
|
71
|
+
*
|
|
72
|
+
* @param key - Cache key
|
|
73
|
+
*/
|
|
74
|
+
delete(key) {
|
|
75
|
+
this.cache.delete(key);
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Clear all cache entries
|
|
79
|
+
*/
|
|
80
|
+
clear() {
|
|
81
|
+
this.cache.clear();
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Get cache size
|
|
85
|
+
*
|
|
86
|
+
* @returns Number of entries in cache
|
|
87
|
+
*/
|
|
88
|
+
size() {
|
|
89
|
+
// Clean up expired entries first
|
|
90
|
+
this.cleanup();
|
|
91
|
+
return this.cache.size;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Clean up expired entries
|
|
95
|
+
*
|
|
96
|
+
* Removes all expired entries from cache.
|
|
97
|
+
*/
|
|
98
|
+
cleanup() {
|
|
99
|
+
const now = Date.now();
|
|
100
|
+
const keysToDelete = [];
|
|
101
|
+
for (const [key, entry] of this.cache.entries()) {
|
|
102
|
+
const age = now - entry.timestamp;
|
|
103
|
+
if (age > entry.ttl) {
|
|
104
|
+
keysToDelete.push(key);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
for (const key of keysToDelete) {
|
|
108
|
+
this.cache.delete(key);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Get cache statistics
|
|
113
|
+
*
|
|
114
|
+
* @returns Cache statistics (size, oldest entry age)
|
|
115
|
+
*/
|
|
116
|
+
getStats() {
|
|
117
|
+
this.cleanup();
|
|
118
|
+
if (this.cache.size === 0) {
|
|
119
|
+
return { size: 0, oldestEntryAge: null };
|
|
120
|
+
}
|
|
121
|
+
const now = Date.now();
|
|
122
|
+
let oldestTimestamp = now;
|
|
123
|
+
for (const entry of this.cache.values()) {
|
|
124
|
+
if (entry.timestamp < oldestTimestamp) {
|
|
125
|
+
oldestTimestamp = entry.timestamp;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return {
|
|
129
|
+
size: this.cache.size,
|
|
130
|
+
oldestEntryAge: now - oldestTimestamp
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Global status cache instance
|
|
136
|
+
*
|
|
137
|
+
* Used across all sync operations for maximum efficiency.
|
|
138
|
+
*/
|
|
139
|
+
export const globalStatusCache = new StatusCache();
|
|
140
|
+
//# sourceMappingURL=status-cache.js.map
|