projecta-rrr 1.16.6 → 1.16.7
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/CHANGELOG.md +36 -0
- package/bin/install.js +43 -1
- package/commands/rrr/add-phase.md +45 -0
- package/commands/rrr/execute-plan.md +37 -0
- package/commands/rrr/insert-phase.md +45 -0
- package/commands/rrr/verify-work.md +36 -0
- package/package.json +5 -2
- package/rrr/lib/install-roots.js +54 -1
- package/rrr/lib/memory-store.js +412 -0
- package/rrr/lib/phase-paths.md +40 -16
- package/scripts/doctor-rrr.js +48 -2
- package/scripts/handoff-preflight.js +52 -7
- package/scripts/prepublish-check.js +19 -0
- package/scripts/publish-rrr.sh +185 -0
- package/scripts/rrr-hud.js +166 -0
- package/scripts/rrr-memory/state-detector.js +413 -0
- package/scripts/test-install-smoke.js +23 -1
- package/scripts/pushpa-jarvis.sh +0 -703
- package/scripts/pushpa-mode.sh +0 -1560
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,42 @@ All notable changes to RRR will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
6
6
|
|
|
7
|
+
## [1.16.7] - 2026-01-28
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
- **Memory System** (`rrr/lib/memory-store.js`) - Central library for tracking:
|
|
11
|
+
- Command history with intent extraction
|
|
12
|
+
- Current user intent persistence
|
|
13
|
+
- Decision log with rationale
|
|
14
|
+
- Drift detection (context drift, stale plans, decision conflicts)
|
|
15
|
+
|
|
16
|
+
- **Visual HUD** (`scripts/rrr-hud.js`) - ASCII progress display showing:
|
|
17
|
+
- Milestone progress bar with percentage
|
|
18
|
+
- Per-phase completion status
|
|
19
|
+
- Drift status (green/yellow/red)
|
|
20
|
+
- Current intent from memory
|
|
21
|
+
|
|
22
|
+
- **Proactive Guidance** (`scripts/rrr-memory/state-detector.js`) - State-aware suggestions:
|
|
23
|
+
- 4D state analysis (project, milestone, phase, action)
|
|
24
|
+
- `guidanceFor(command)` for 17 command scenarios
|
|
25
|
+
- Orphan phase detection and migration guidance
|
|
26
|
+
|
|
27
|
+
- **Memory-Enriched Preflight** (`scripts/handoff-preflight.js`) - Drift detection on handoff
|
|
28
|
+
|
|
29
|
+
### Changed
|
|
30
|
+
- **Phase path resolution** (`rrr/lib/phase-paths.md`) - Fixed bug where phases were created outside milestones when no `:construction:` marker existed. Now errors if `.planning/milestones/` exists but no active milestone detected.
|
|
31
|
+
|
|
32
|
+
- **execute-plan** - Tracks intent before execution for drift detection
|
|
33
|
+
|
|
34
|
+
- **verify-work** - Shows memory context during verification
|
|
35
|
+
|
|
36
|
+
- **add-phase / insert-phase** - Validates no orphan phases exist before allowing new phase creation
|
|
37
|
+
|
|
38
|
+
- **Duplicate prevention** (`bin/install.js`, `scripts/doctor-rrr.js`) - Auto-cleans old quarantine directories (>7 days) to prevent duplicate command scanning
|
|
39
|
+
|
|
40
|
+
### Fixed
|
|
41
|
+
- **Duplicate commands** - 282 command references reduced to 41 by cleaning old quarantines
|
|
42
|
+
|
|
7
43
|
## [1.16.6] - 2026-01-28
|
|
8
44
|
|
|
9
45
|
### Added
|
package/bin/install.js
CHANGED
|
@@ -11,7 +11,8 @@ const {
|
|
|
11
11
|
detectAndQuarantineDuplicates,
|
|
12
12
|
writeInstallInfo,
|
|
13
13
|
getCurrentVersion,
|
|
14
|
-
getCanonicalRoots
|
|
14
|
+
getCanonicalRoots,
|
|
15
|
+
cleanOldQuarantines
|
|
15
16
|
} = require('../rrr/lib/install-roots');
|
|
16
17
|
|
|
17
18
|
/**
|
|
@@ -909,6 +910,38 @@ function install(isGlobal) {
|
|
|
909
910
|
console.log(` ${green}✓${reset} Installed rrr/scripts/test-install-smoke.js`);
|
|
910
911
|
}
|
|
911
912
|
|
|
913
|
+
// PATCH-01: Copy handoff-preflight.js to ~/.claude/rrr/scripts/
|
|
914
|
+
// Memory-enriched preflight for drift detection
|
|
915
|
+
const handoffSrc = path.join(src, 'scripts', 'handoff-preflight.js');
|
|
916
|
+
if (fs.existsSync(handoffSrc)) {
|
|
917
|
+
const scriptsDestDir = path.join(claudeDir, 'rrr', 'scripts');
|
|
918
|
+
fs.mkdirSync(scriptsDestDir, { recursive: true });
|
|
919
|
+
const handoffDest = path.join(scriptsDestDir, 'handoff-preflight.js');
|
|
920
|
+
fs.copyFileSync(handoffSrc, handoffDest);
|
|
921
|
+
console.log(` ${green}✓${reset} Installed rrr/scripts/handoff-preflight.js`);
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
// PATCH-01: Copy rrr-hud.js to ~/.claude/rrr/scripts/
|
|
925
|
+
// Visual HUD for project state display
|
|
926
|
+
const hudSrc = path.join(src, 'scripts', 'rrr-hud.js');
|
|
927
|
+
if (fs.existsSync(hudSrc)) {
|
|
928
|
+
const scriptsDestDir = path.join(claudeDir, 'rrr', 'scripts');
|
|
929
|
+
fs.mkdirSync(scriptsDestDir, { recursive: true });
|
|
930
|
+
const hudDest = path.join(scriptsDestDir, 'rrr-hud.js');
|
|
931
|
+
fs.copyFileSync(hudSrc, hudDest);
|
|
932
|
+
console.log(` ${green}✓${reset} Installed rrr/scripts/rrr-hud.js`);
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
// PATCH-01: Copy rrr-memory/ directory to ~/.claude/rrr/scripts/
|
|
936
|
+
// Memory store and state detector for proactive guidance
|
|
937
|
+
const rrrMemorySrc = path.join(src, 'scripts', 'rrr-memory');
|
|
938
|
+
if (fs.existsSync(rrrMemorySrc)) {
|
|
939
|
+
const scriptsDestDir = path.join(claudeDir, 'rrr', 'scripts');
|
|
940
|
+
const rrrMemoryDest = path.join(scriptsDestDir, 'rrr-memory');
|
|
941
|
+
copyWithPathReplacement(rrrMemorySrc, rrrMemoryDest, pathPrefix);
|
|
942
|
+
console.log(` ${green}✓${reset} Installed rrr/scripts/rrr-memory/`);
|
|
943
|
+
}
|
|
944
|
+
|
|
912
945
|
// Copy skills to ~/.claude/skills (skills system)
|
|
913
946
|
const skillsSrc = path.join(src, 'rrr', 'skills');
|
|
914
947
|
if (fs.existsSync(skillsSrc)) {
|
|
@@ -963,6 +996,15 @@ function install(isGlobal) {
|
|
|
963
996
|
if (quarantineResult.quarantined.length > 0) {
|
|
964
997
|
console.log(` ${green}✓${reset} Quarantined ${quarantineResult.quarantined.length} legacy/duplicate root(s)`);
|
|
965
998
|
}
|
|
999
|
+
|
|
1000
|
+
// Clean up old quarantine directories (>7 days) to prevent duplicate command scanning
|
|
1001
|
+
console.log(` ${green}✓${reset} Cleaning old quarantine directories...`);
|
|
1002
|
+
const cleanupResult = cleanOldQuarantines({
|
|
1003
|
+
configDir: explicitConfigDir || process.env.CLAUDE_CONFIG_DIR
|
|
1004
|
+
});
|
|
1005
|
+
if (cleanupResult.cleaned.length > 0) {
|
|
1006
|
+
console.log(` ${green}✓${reset} Cleaned ${cleanupResult.cleaned.length} old quarantine folder(s)`);
|
|
1007
|
+
}
|
|
966
1008
|
}
|
|
967
1009
|
|
|
968
1010
|
// Register rrr-search MCP server and run semantic migration
|
|
@@ -33,6 +33,7 @@ Purpose: Add planned work discovered during execution that belongs at the end of
|
|
|
33
33
|
@.planning/STATE.md
|
|
34
34
|
@rrr/lib/phase-paths.md
|
|
35
35
|
@rrr/lib/milestone-utils.md
|
|
36
|
+
@rrr/lib/memory-store.js
|
|
36
37
|
</execution_context>
|
|
37
38
|
|
|
38
39
|
<process>
|
|
@@ -68,6 +69,50 @@ Example: /rrr:add-phase Add authentication system
|
|
|
68
69
|
Exit.
|
|
69
70
|
</step>
|
|
70
71
|
|
|
72
|
+
<step name="check_orphan_phases_memory">
|
|
73
|
+
**Check for orphan phases using state-detector (PATCH-01 enhancement)**
|
|
74
|
+
|
|
75
|
+
Before creating phases, check if there are orphan phases that need migration:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
# Check for orphan phases
|
|
79
|
+
node -e "
|
|
80
|
+
const { StateDetector } = require(process.env.HOME + '/.claude/rrr/scripts/rrr-memory/state-detector');
|
|
81
|
+
const detector = new StateDetector();
|
|
82
|
+
const state = detector.detectPhaseState('.planning');
|
|
83
|
+
const milestone = detector.detectMilestoneState('.planning');
|
|
84
|
+
|
|
85
|
+
if (milestone === 'NO_MILESTONE' && state !== 'NO_PHASES') {
|
|
86
|
+
console.log('HAS_ORPHAN_PHASES=true');
|
|
87
|
+
console.log('RUN: /rrr:new-milestone or /rrr:migrate-phases');
|
|
88
|
+
}
|
|
89
|
+
" 2>/dev/null
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
If `HAS_ORPHAN_PHASES=true` is output, show guidance and exit (unless --force).
|
|
93
|
+
|
|
94
|
+
**If orphan phases detected:**
|
|
95
|
+
|
|
96
|
+
```
|
|
97
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
98
|
+
⚠ ORPHAN PHASES DETECTED
|
|
99
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
100
|
+
|
|
101
|
+
Found phases not assigned to any milestone.
|
|
102
|
+
New phases cannot be created until orphans are assigned.
|
|
103
|
+
|
|
104
|
+
To migrate orphan phases:
|
|
105
|
+
1. /rrr:new-milestone - Create milestone for orphan phases
|
|
106
|
+
2. /rrr:migrate-phases - Assign phases to milestone
|
|
107
|
+
|
|
108
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Exit command.
|
|
112
|
+
|
|
113
|
+
**If no orphans or --force:** Continue to validate_milestone_first step.
|
|
114
|
+
</step>
|
|
115
|
+
|
|
71
116
|
<step name="validate_milestone_first">
|
|
72
117
|
**MILESTONE-FIRST ENFORCEMENT (v1.11 breaking change)**
|
|
73
118
|
|
|
@@ -36,6 +36,7 @@ Plan path: $ARGUMENTS
|
|
|
36
36
|
|
|
37
37
|
@.planning/STATE.md
|
|
38
38
|
@.planning/config.json (if exists)
|
|
39
|
+
@rrr/lib/memory-store.js
|
|
39
40
|
</context>
|
|
40
41
|
|
|
41
42
|
<process>
|
|
@@ -46,6 +47,42 @@ Plan path: $ARGUMENTS
|
|
|
46
47
|
```
|
|
47
48
|
Cross-platform: works in PowerShell on Windows. Path resolved to installed location.
|
|
48
49
|
|
|
50
|
+
0. **Track command in memory (before execution)**
|
|
51
|
+
Parse the plan to extract intent from frontmatter and context, then track this execution:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
# Extract plan ID and objective for intent tracking
|
|
55
|
+
PLAN_ID=$(basename "$ARGUMENTS" | sed 's/-PLAN\.md$//')
|
|
56
|
+
PLAN_DIR=$(dirname "$ARGUMENTS")
|
|
57
|
+
PHASE_DIR=$(basename "$PLAN_DIR")
|
|
58
|
+
|
|
59
|
+
# Get intent from plan frontmatter (try files_modified, then title)
|
|
60
|
+
INTENT=""
|
|
61
|
+
if [ -f "$ARGUMENTS" ]; then
|
|
62
|
+
INTENT=$(grep -E "^files_modified:" "$ARGUMENTS" 2>/dev/null | head -1 | sed 's/files_modified: *\[*\]*/Intent from plan/' || \
|
|
63
|
+
grep -E "^title:" "$ARGUMENTS" 2>/dev/null | head -1 | sed 's/title: *//' || \
|
|
64
|
+
echo "Executing plan $PLAN_ID")
|
|
65
|
+
fi
|
|
66
|
+
|
|
67
|
+
# Track in memory (if memory-store.js is available)
|
|
68
|
+
node -e "
|
|
69
|
+
const { initMemory, trackCommand } = require(process.env.HOME + '/.claude/rrr/lib/memory-store');
|
|
70
|
+
(async () => {
|
|
71
|
+
try {
|
|
72
|
+
const memory = await initMemory();
|
|
73
|
+
await trackCommand(memory, {
|
|
74
|
+
command: '/rrr:execute-plan',
|
|
75
|
+
target: '$PLAN_ID',
|
|
76
|
+
intent: '$INTENT',
|
|
77
|
+
files: []
|
|
78
|
+
});
|
|
79
|
+
} catch (e) {
|
|
80
|
+
// Memory tracking is optional
|
|
81
|
+
}
|
|
82
|
+
})();
|
|
83
|
+
" 2>/dev/null || echo "[MEMORY] Tracking skipped"
|
|
84
|
+
```
|
|
85
|
+
|
|
49
86
|
0.5. **Scope Check (validate plan is in scope)**
|
|
50
87
|
|
|
51
88
|
Before executing, validate the plan's files against current scope:
|
|
@@ -23,6 +23,7 @@ Purpose: Handle urgent work discovered during execution without renumbering enti
|
|
|
23
23
|
@.planning/STATE.md
|
|
24
24
|
@rrr/lib/phase-paths.md
|
|
25
25
|
@rrr/lib/milestone-utils.md
|
|
26
|
+
@rrr/lib/memory-store.js
|
|
26
27
|
</execution_context>
|
|
27
28
|
|
|
28
29
|
<process>
|
|
@@ -74,6 +75,50 @@ fi
|
|
|
74
75
|
|
|
75
76
|
</step>
|
|
76
77
|
|
|
78
|
+
<step name="check_orphan_phases_memory">
|
|
79
|
+
**Check for orphan phases using state-detector (PATCH-01 enhancement)**
|
|
80
|
+
|
|
81
|
+
Same validation as add-phase - check for orphan phases before allowing new insert:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
# Check for orphan phases
|
|
85
|
+
node -e "
|
|
86
|
+
const { StateDetector } = require(process.env.HOME + '/.claude/rrr/scripts/rrr-memory/state-detector');
|
|
87
|
+
const detector = new StateDetector();
|
|
88
|
+
const state = detector.detectPhaseState('.planning');
|
|
89
|
+
const milestone = detector.detectMilestoneState('.planning');
|
|
90
|
+
|
|
91
|
+
if (milestone === 'NO_MILESTONE' && state !== 'NO_PHASES') {
|
|
92
|
+
console.log('HAS_ORPHAN_PHASES=true');
|
|
93
|
+
console.log('RUN: /rrr:new-milestone or /rrr:migrate-phases');
|
|
94
|
+
}
|
|
95
|
+
" 2>/dev/null
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
If `HAS_ORPHAN_PHASES=true`, show guidance and exit (unless --force).
|
|
99
|
+
|
|
100
|
+
**If orphan phases detected:**
|
|
101
|
+
|
|
102
|
+
```
|
|
103
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
104
|
+
⚠ ORPHAN PHASES DETECTED
|
|
105
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
106
|
+
|
|
107
|
+
Found phases not assigned to any milestone.
|
|
108
|
+
Inserting new phase not allowed until orphans are assigned.
|
|
109
|
+
|
|
110
|
+
To migrate orphan phases:
|
|
111
|
+
1. /rrr:new-milestone - Create milestone for orphan phases
|
|
112
|
+
2. /rrr:migrate-phases - Assign phases to milestone
|
|
113
|
+
|
|
114
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Exit command.
|
|
118
|
+
|
|
119
|
+
**If no orphans or --force:** Continue to validate_milestone_first step.
|
|
120
|
+
</step>
|
|
121
|
+
|
|
77
122
|
<step name="validate_milestone_first">
|
|
78
123
|
**MILESTONE-FIRST ENFORCEMENT (v1.11 breaking change)**
|
|
79
124
|
|
|
@@ -71,6 +71,7 @@ Validate built features through **audit mode by default**, or interactive UAT wi
|
|
|
71
71
|
|
|
72
72
|
<execution_context>
|
|
73
73
|
@rrr/lib/phase-paths.md
|
|
74
|
+
@rrr/lib/memory-store.js
|
|
74
75
|
</execution_context>
|
|
75
76
|
|
|
76
77
|
<!-- ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -140,6 +141,41 @@ For manual testing, use: /rrr:verify-work --uat [phase]
|
|
|
140
141
|
|
|
141
142
|
---
|
|
142
143
|
|
|
144
|
+
### Step 0.5: Show Memory Context (PATCH-01 enhancement)
|
|
145
|
+
|
|
146
|
+
Display current intent and recent commands to provide context during verification:
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
# Show memory context if available
|
|
150
|
+
node -e "
|
|
151
|
+
const { initMemory, getSummary } = require(process.env.HOME + '/.claude/rrr/lib/memory-store');
|
|
152
|
+
(async () => {
|
|
153
|
+
try {
|
|
154
|
+
const memory = await initMemory();
|
|
155
|
+
const summary = await getSummary(memory);
|
|
156
|
+
console.log('');
|
|
157
|
+
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
158
|
+
console.log(' RRR ► MEMORY CONTEXT');
|
|
159
|
+
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
160
|
+
console.log('Intent:', summary.currentIntent || '(no intent tracked)');
|
|
161
|
+
console.log('Recent:', summary.recentCommands.slice(0, 2).map(c =>
|
|
162
|
+
c.cmd + (c.target ? '(' + c.target + ')' : '')
|
|
163
|
+
).join(' → ') || 'none');
|
|
164
|
+
if (summary.drift && summary.drift.drifting) {
|
|
165
|
+
console.log('Drift:', summary.drift.message);
|
|
166
|
+
}
|
|
167
|
+
console.log('───────────────────────────────────────────────────────');
|
|
168
|
+
} catch (e) {
|
|
169
|
+
// Memory not available, skip silently
|
|
170
|
+
}
|
|
171
|
+
})();
|
|
172
|
+
" 2>/dev/null || true
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
This helps verify-work understand what the user was working on.
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
143
179
|
### Step 1: Determine Scope
|
|
144
180
|
|
|
145
181
|
**Parse target from arguments (with flags removed):**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "projecta-rrr",
|
|
3
|
-
"version": "1.16.
|
|
3
|
+
"version": "1.16.7",
|
|
4
4
|
"description": "A meta-prompting, context engineering and spec-driven development system for Claude Code by Projecta.ai",
|
|
5
5
|
"bin": {
|
|
6
6
|
"projecta-rrr": "bin/install.js"
|
|
@@ -22,7 +22,10 @@
|
|
|
22
22
|
"sessions:index": "bash scripts/index-sessions.sh",
|
|
23
23
|
"sessions:status": "bash scripts/index-sessions.sh --status",
|
|
24
24
|
"semantic:verify": "bash scripts/verify-semantic-search.sh",
|
|
25
|
-
"prepublish:check": "node scripts/prepublish-check.js"
|
|
25
|
+
"prepublish:check": "node scripts/prepublish-check.js",
|
|
26
|
+
"browser:uat": "bash scripts/browser-uat.sh",
|
|
27
|
+
"scaffold:e2e": "bash scripts/scaffold-e2e.sh",
|
|
28
|
+
"chrome:visual": "bash scripts/chrome-visual-check.sh"
|
|
26
29
|
},
|
|
27
30
|
"files": [
|
|
28
31
|
"bin",
|
package/rrr/lib/install-roots.js
CHANGED
|
@@ -535,6 +535,58 @@ if (require.main === module) {
|
|
|
535
535
|
process.exit(summary.verification.valid ? 0 : 1);
|
|
536
536
|
}
|
|
537
537
|
|
|
538
|
+
/**
|
|
539
|
+
* Clean up old quarantine directories
|
|
540
|
+
* Removes quarantine folders older than maxAgeMs to prevent duplicates from being scanned
|
|
541
|
+
* @param {object} options
|
|
542
|
+
* @param {string} [options.configDir] - Explicit config directory
|
|
543
|
+
* @param {number} [options.maxAgeMs=7*24*60*60*1000] - Max age in ms (default 7 days)
|
|
544
|
+
* @returns {{cleaned: number, errors: string[]}}
|
|
545
|
+
*/
|
|
546
|
+
function cleanOldQuarantines(options = {}, maxAgeMs = 7 * 24 * 60 * 60 * 1000) {
|
|
547
|
+
const roots = getCanonicalRoots(options);
|
|
548
|
+
const quarantineRoot = roots.quarantineRoot;
|
|
549
|
+
const cleaned = [];
|
|
550
|
+
const errors = [];
|
|
551
|
+
|
|
552
|
+
if (!fs.existsSync(quarantineRoot)) {
|
|
553
|
+
return { cleaned: 0, errors: [] };
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
const now = Date.now();
|
|
557
|
+
const entries = fs.readdirSync(quarantineRoot, { withFileTypes: true });
|
|
558
|
+
|
|
559
|
+
for (const entry of entries) {
|
|
560
|
+
if (!entry.isDirectory()) continue;
|
|
561
|
+
if (entry.name === 'QUARANTINE_LOG.json') continue;
|
|
562
|
+
|
|
563
|
+
const dirPath = path.join(quarantineRoot, entry.name);
|
|
564
|
+
const stats = fs.statSync(dirPath);
|
|
565
|
+
const age = now - stats.mtimeMs;
|
|
566
|
+
|
|
567
|
+
if (age > maxAgeMs) {
|
|
568
|
+
try {
|
|
569
|
+
// Delete directory contents and directory itself
|
|
570
|
+
const files = fs.readdirSync(dirPath);
|
|
571
|
+
for (const file of files) {
|
|
572
|
+
const filePath = path.join(dirPath, file);
|
|
573
|
+
if (fs.statSync(filePath).isDirectory()) {
|
|
574
|
+
fs.rmSync(filePath, { recursive: true });
|
|
575
|
+
} else {
|
|
576
|
+
fs.unlinkSync(filePath);
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
fs.rmdirSync(dirPath);
|
|
580
|
+
cleaned.push(entry.name);
|
|
581
|
+
} catch (e) {
|
|
582
|
+
errors.push(`Failed to clean ${entry.name}: ${e.message}`);
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
return { cleaned, errors };
|
|
588
|
+
}
|
|
589
|
+
|
|
538
590
|
module.exports = {
|
|
539
591
|
getCanonicalRoots,
|
|
540
592
|
detectAllCandidateRoots,
|
|
@@ -545,5 +597,6 @@ module.exports = {
|
|
|
545
597
|
getInstallInfo,
|
|
546
598
|
detectAndQuarantineDuplicates,
|
|
547
599
|
verifyInstallation,
|
|
548
|
-
getInstallationSummary
|
|
600
|
+
getInstallationSummary,
|
|
601
|
+
cleanOldQuarantines
|
|
549
602
|
};
|