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 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.6",
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",
@@ -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
  };