projecta-rrr 1.18.0 → 1.18.1
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 +14 -0
- package/bin/install.js +106 -0
- package/hooks/rrr-capture-test-results.sh +44 -0
- package/hooks/rrr-detect-drift.sh +15 -0
- package/hooks/rrr-update-cache-stats.sh +30 -9
- package/hooks/statusline.sh +6 -15
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,20 @@ 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.18.1] - 2026-01-30
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- **Drift detection now updates HUD state** - PostToolUse hook was writing to `.planning/.drift-status.json` but statusline reads from `~/.claude/rrr/hud-state.json`. Now updates both.
|
|
12
|
+
- **Cache stats hook missing from SessionStart** - Added `rrr-update-cache-stats.sh` to SessionStart hooks for IDX % refresh
|
|
13
|
+
- **IDX showing file count instead of hit rate** - Changed to show cache hit rate percentage
|
|
14
|
+
- **Index path detection** - Fixed to find LanceDB at actual location (`~/.claude/rrr/lib/search/.rrr/search/lancedb/`)
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
|
|
18
|
+
- **Scheduled indexing** (macOS) - Launchd agent runs semantic + session indexing every 4 hours
|
|
19
|
+
- **Test results capture hook** - `rrr-capture-test-results.sh` updates HUD with test pass rate after `npm test`
|
|
20
|
+
|
|
7
21
|
## [1.18.0] - 2026-01-30
|
|
8
22
|
|
|
9
23
|
### Added
|
package/bin/install.js
CHANGED
|
@@ -1359,6 +1359,27 @@ function install(isGlobal) {
|
|
|
1359
1359
|
});
|
|
1360
1360
|
console.log(` ${green}✓${reset} Configured HUD state sync hook`);
|
|
1361
1361
|
}
|
|
1362
|
+
|
|
1363
|
+
// Add cache stats update hook (updates IDX % in statusline)
|
|
1364
|
+
const cacheStatsCommand = isGlobal
|
|
1365
|
+
? '$HOME/.claude/hooks/rrr-update-cache-stats.sh'
|
|
1366
|
+
: `${localDirName}/hooks/rrr-update-cache-stats.sh`;
|
|
1367
|
+
|
|
1368
|
+
const hasCacheStatsHook = settings.hooks.SessionStart.some(entry =>
|
|
1369
|
+
entry.hooks && entry.hooks.some(h => h.command && h.command.includes('rrr-update-cache-stats'))
|
|
1370
|
+
);
|
|
1371
|
+
|
|
1372
|
+
if (!hasCacheStatsHook && hookFileExists(claudeDir, 'rrr-update-cache-stats.sh')) {
|
|
1373
|
+
settings.hooks.SessionStart.push({
|
|
1374
|
+
hooks: [
|
|
1375
|
+
{
|
|
1376
|
+
type: 'command',
|
|
1377
|
+
command: cacheStatsCommand
|
|
1378
|
+
}
|
|
1379
|
+
]
|
|
1380
|
+
});
|
|
1381
|
+
console.log(` ${green}✓${reset} Configured cache stats hook`);
|
|
1382
|
+
}
|
|
1362
1383
|
}
|
|
1363
1384
|
|
|
1364
1385
|
// PATCH-01: Configure PostToolUse hook for drift detection
|
|
@@ -1590,6 +1611,86 @@ function install(isGlobal) {
|
|
|
1590
1611
|
/**
|
|
1591
1612
|
* Print doctor summary showing where hooks were installed and configured paths
|
|
1592
1613
|
*/
|
|
1614
|
+
/**
|
|
1615
|
+
* Setup scheduled indexing via macOS launchd
|
|
1616
|
+
* Runs semantic index and session indexing every 4 hours
|
|
1617
|
+
*/
|
|
1618
|
+
function setupScheduledIndexing() {
|
|
1619
|
+
const launchAgentsDir = path.join(os.homedir(), 'Library', 'LaunchAgents');
|
|
1620
|
+
const plistPath = path.join(launchAgentsDir, 'com.projecta.rrr-index.plist');
|
|
1621
|
+
|
|
1622
|
+
// Only setup if not already configured
|
|
1623
|
+
if (fs.existsSync(plistPath)) {
|
|
1624
|
+
return;
|
|
1625
|
+
}
|
|
1626
|
+
|
|
1627
|
+
try {
|
|
1628
|
+
// Ensure LaunchAgents directory exists
|
|
1629
|
+
if (!fs.existsSync(launchAgentsDir)) {
|
|
1630
|
+
fs.mkdirSync(launchAgentsDir, { recursive: true });
|
|
1631
|
+
}
|
|
1632
|
+
|
|
1633
|
+
const plistContent = `<?xml version="1.0" encoding="UTF-8"?>
|
|
1634
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
1635
|
+
<plist version="1.0">
|
|
1636
|
+
<dict>
|
|
1637
|
+
<key>Label</key>
|
|
1638
|
+
<string>com.projecta.rrr-index</string>
|
|
1639
|
+
<key>ProgramArguments</key>
|
|
1640
|
+
<array>
|
|
1641
|
+
<string>/bin/bash</string>
|
|
1642
|
+
<string>-c</string>
|
|
1643
|
+
<string>
|
|
1644
|
+
# Run semantic index if grepai available
|
|
1645
|
+
if command -v grepai &>/dev/null; then
|
|
1646
|
+
grepai index --quiet 2>/dev/null
|
|
1647
|
+
fi
|
|
1648
|
+
|
|
1649
|
+
# Run session indexing (Datasphere)
|
|
1650
|
+
INSTALLED_RRR=$(npm root -g 2>/dev/null)/projecta-rrr
|
|
1651
|
+
if [[ -f "$INSTALLED_RRR/scripts/index-sessions.sh" ]]; then
|
|
1652
|
+
bash "$INSTALLED_RRR/scripts/index-sessions.sh" --all --quiet 2>/dev/null
|
|
1653
|
+
fi
|
|
1654
|
+
|
|
1655
|
+
# Update cache stats for HUD
|
|
1656
|
+
~/.claude/hooks/rrr-update-cache-stats.sh 2>/dev/null
|
|
1657
|
+
</string>
|
|
1658
|
+
</array>
|
|
1659
|
+
<key>StartInterval</key>
|
|
1660
|
+
<integer>14400</integer>
|
|
1661
|
+
<key>RunAtLoad</key>
|
|
1662
|
+
<true/>
|
|
1663
|
+
<key>StandardOutPath</key>
|
|
1664
|
+
<string>/tmp/rrr-index.log</string>
|
|
1665
|
+
<key>StandardErrorPath</key>
|
|
1666
|
+
<string>/tmp/rrr-index.err</string>
|
|
1667
|
+
<key>LowPriorityIO</key>
|
|
1668
|
+
<true/>
|
|
1669
|
+
<key>ProcessType</key>
|
|
1670
|
+
<string>Background</string>
|
|
1671
|
+
<key>EnvironmentVariables</key>
|
|
1672
|
+
<dict>
|
|
1673
|
+
<key>PATH</key>
|
|
1674
|
+
<string>/usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin</string>
|
|
1675
|
+
</dict>
|
|
1676
|
+
</dict>
|
|
1677
|
+
</plist>`;
|
|
1678
|
+
|
|
1679
|
+
fs.writeFileSync(plistPath, plistContent);
|
|
1680
|
+
|
|
1681
|
+
// Load the agent
|
|
1682
|
+
const { execSync } = require('child_process');
|
|
1683
|
+
try {
|
|
1684
|
+
execSync(`launchctl load "${plistPath}"`, { stdio: 'pipe' });
|
|
1685
|
+
console.log(` ${green}✓${reset} Configured scheduled indexing (every 4 hours)`);
|
|
1686
|
+
} catch (e) {
|
|
1687
|
+
// Agent might already be loaded
|
|
1688
|
+
}
|
|
1689
|
+
} catch (e) {
|
|
1690
|
+
// Silent fail - scheduled indexing is optional
|
|
1691
|
+
}
|
|
1692
|
+
}
|
|
1693
|
+
|
|
1593
1694
|
function printDoctorSummary(claudeDir, settings, localDirName, isGlobal) {
|
|
1594
1695
|
console.log(`\n ${cyan}Hook Configuration Summary:${reset}`);
|
|
1595
1696
|
|
|
@@ -1727,6 +1828,11 @@ function finishInstall(settingsPath, settings, statuslineCommand, notifyCommand,
|
|
|
1727
1828
|
}
|
|
1728
1829
|
}
|
|
1729
1830
|
|
|
1831
|
+
// Setup scheduled indexing (macOS launchd) - runs every 4 hours
|
|
1832
|
+
if (process.platform === 'darwin' && isGlobal) {
|
|
1833
|
+
setupScheduledIndexing();
|
|
1834
|
+
}
|
|
1835
|
+
|
|
1730
1836
|
console.log(`
|
|
1731
1837
|
${green}Done!${reset}
|
|
1732
1838
|
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# rrr-capture-test-results.sh - Capture test results and update HUD
|
|
4
|
+
#
|
|
5
|
+
# Usage: npm test 2>&1 | tee /tmp/test-output.txt; rrr-capture-test-results.sh
|
|
6
|
+
#
|
|
7
|
+
|
|
8
|
+
set +e
|
|
9
|
+
|
|
10
|
+
HUD_FILE="$HOME/.claude/rrr/hud-state.json"
|
|
11
|
+
TEST_OUTPUT="${1:-/tmp/test-output.txt}"
|
|
12
|
+
|
|
13
|
+
if [[ ! -f "$TEST_OUTPUT" ]]; then
|
|
14
|
+
exit 0
|
|
15
|
+
fi
|
|
16
|
+
|
|
17
|
+
# Parse Jest output
|
|
18
|
+
if grep -q "Tests:" "$TEST_OUTPUT"; then
|
|
19
|
+
# Jest format: Tests: X failed, Y passed, Z total
|
|
20
|
+
passed=$(grep "Tests:" "$TEST_OUTPUT" | grep -oE '[0-9]+ passed' | grep -oE '[0-9]+' || echo "0")
|
|
21
|
+
failed=$(grep "Tests:" "$TEST_OUTPUT" | grep -oE '[0-9]+ failed' | grep -oE '[0-9]+' || echo "0")
|
|
22
|
+
total=$((passed + failed))
|
|
23
|
+
|
|
24
|
+
if [[ "$total" -gt 0 ]]; then
|
|
25
|
+
pass_rate=$((passed * 100 / total))
|
|
26
|
+
|
|
27
|
+
# Update HUD state
|
|
28
|
+
if [[ -f "$HUD_FILE" ]] && command -v jq &>/dev/null; then
|
|
29
|
+
jq --argjson passed "$passed" \
|
|
30
|
+
--argjson failed "$failed" \
|
|
31
|
+
--argjson total "$total" \
|
|
32
|
+
--argjson rate "$pass_rate" '
|
|
33
|
+
.tests.passed = $passed |
|
|
34
|
+
.tests.failed = $failed |
|
|
35
|
+
.tests.total = $total |
|
|
36
|
+
.tests.pass_rate = $rate
|
|
37
|
+
' "$HUD_FILE" > "${HUD_FILE}.tmp" && mv "${HUD_FILE}.tmp" "$HUD_FILE"
|
|
38
|
+
|
|
39
|
+
echo "Updated HUD: ${passed}/${total} tests passed (${pass_rate}%)"
|
|
40
|
+
fi
|
|
41
|
+
fi
|
|
42
|
+
fi
|
|
43
|
+
|
|
44
|
+
exit 0
|
|
@@ -116,6 +116,21 @@ if [ -n "$DRIFT_RESULT" ]; then
|
|
|
116
116
|
echo "$DRIFT_RESULT" > "$DRIFT_STATUS_FILE" 2>/dev/null
|
|
117
117
|
fi
|
|
118
118
|
|
|
119
|
+
# Update HUD state with drift info (so statusline can read it)
|
|
120
|
+
HUD_STATE_FILE="${CLAUDE_DIR}/rrr/hud-state.json"
|
|
121
|
+
if [ -f "$HUD_STATE_FILE" ] && command -v jq &>/dev/null; then
|
|
122
|
+
# Count changed files
|
|
123
|
+
FILE_COUNT=$(echo "$FILES" | tr ',' '\n' | wc -l | tr -d ' ')
|
|
124
|
+
|
|
125
|
+
# Update drift section in hud-state.json
|
|
126
|
+
jq --argjson count "$FILE_COUNT" --arg files "$FILES" '
|
|
127
|
+
.drift.detected = ($count > 0) |
|
|
128
|
+
.drift.files_changed = $count |
|
|
129
|
+
.drift.details = (if $files != "" then ($files | split(",")[0:5]) else [] end) |
|
|
130
|
+
.status = (if $count > 5 then "drifting" else .status end)
|
|
131
|
+
' "$HUD_STATE_FILE" > "${HUD_STATE_FILE}.tmp" && mv "${HUD_STATE_FILE}.tmp" "$HUD_STATE_FILE"
|
|
132
|
+
fi
|
|
133
|
+
|
|
119
134
|
# ============================================================================
|
|
120
135
|
# Episode Emission
|
|
121
136
|
# ============================================================================
|
|
@@ -21,11 +21,16 @@ if [[ -f "$MCP_SERVER" ]]; then
|
|
|
21
21
|
const path = require('path');
|
|
22
22
|
const fs = require('fs');
|
|
23
23
|
|
|
24
|
-
// Find the index database
|
|
25
|
-
const
|
|
26
|
-
|
|
24
|
+
// Find the index database (check multiple locations)
|
|
25
|
+
const possiblePaths = [
|
|
26
|
+
path.join(process.env.HOME, '.claude', 'rrr', 'lib', 'search', '.rrr', 'search', 'lancedb', 'file_index.lance'),
|
|
27
|
+
path.join(process.env.HOME, '.claude', 'rrr', '.semantic-index', 'rrr-search.lance'),
|
|
28
|
+
path.join(process.env.HOME, '.grepai', 'index.lance')
|
|
29
|
+
];
|
|
30
|
+
const dbPath = possiblePaths.find(p => fs.existsSync(p));
|
|
31
|
+
const indexDir = dbPath ? path.dirname(dbPath) : null;
|
|
27
32
|
|
|
28
|
-
if (!
|
|
33
|
+
if (!dbPath || !indexDir) {
|
|
29
34
|
// No index yet
|
|
30
35
|
console.log(JSON.stringify({
|
|
31
36
|
fileCount: 0,
|
|
@@ -47,11 +52,27 @@ const lastUpdated = stats.mtime.toISOString();
|
|
|
47
52
|
let fileCount = 0;
|
|
48
53
|
let chunkCount = 0;
|
|
49
54
|
try {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
+
// Try manifest.json in various locations
|
|
56
|
+
const manifestPaths = [
|
|
57
|
+
path.join(indexDir, 'manifest.json'),
|
|
58
|
+
path.join(indexDir, '..', 'manifest.json'),
|
|
59
|
+
path.join(indexDir, '..', '..', 'manifest.json')
|
|
60
|
+
];
|
|
61
|
+
for (const manifestPath of manifestPaths) {
|
|
62
|
+
if (fs.existsSync(manifestPath)) {
|
|
63
|
+
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
64
|
+
fileCount = manifest.files ? Object.keys(manifest.files).length : 0;
|
|
65
|
+
chunkCount = manifest.totalChunks || fileCount * 5;
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
// Fallback: count .lance data files
|
|
70
|
+
if (fileCount === 0) {
|
|
71
|
+
const dataDir = path.join(dbPath, 'data');
|
|
72
|
+
if (fs.existsSync(dataDir)) {
|
|
73
|
+
fileCount = fs.readdirSync(dataDir).filter(f => f.endsWith('.lance')).length * 100; // estimate
|
|
74
|
+
chunkCount = fileCount * 5;
|
|
75
|
+
}
|
|
55
76
|
}
|
|
56
77
|
} catch (e) {}
|
|
57
78
|
|
package/hooks/statusline.sh
CHANGED
|
@@ -82,27 +82,18 @@ if [[ -f "$HUD_FILE" ]]; then
|
|
|
82
82
|
fi
|
|
83
83
|
fi
|
|
84
84
|
|
|
85
|
-
# Show index health
|
|
86
|
-
file_count=$(jq -r '.fileCount // 0' "$CACHE_FILE" 2>/dev/null)
|
|
85
|
+
# Show index health as hit rate percentage (ASCII only for Claude Code compatibility)
|
|
87
86
|
healthy=$(jq -r '.healthy // false' "$CACHE_FILE" 2>/dev/null)
|
|
88
87
|
|
|
89
|
-
if [[ "$
|
|
90
|
-
#
|
|
91
|
-
if [[ "$file_count" -gt 1000 ]]; then
|
|
92
|
-
file_display=$(echo "scale=1; $file_count / 1000" | bc 2>/dev/null || echo "$file_count")
|
|
93
|
-
file_display="${file_display}k"
|
|
94
|
-
else
|
|
95
|
-
file_display="$file_count"
|
|
96
|
-
fi
|
|
97
|
-
|
|
98
|
-
# Show health indicator (ASCII)
|
|
88
|
+
if [[ "$cache_rate" -gt 0 ]]; then
|
|
89
|
+
# Show hit rate percentage
|
|
99
90
|
if [[ "$healthy" == "true" ]]; then
|
|
100
|
-
brain="IDX:${
|
|
91
|
+
brain="IDX:${cache_rate}%"
|
|
101
92
|
else
|
|
102
|
-
brain="IDX:${
|
|
93
|
+
brain="IDX:${cache_rate}%!"
|
|
103
94
|
fi
|
|
104
95
|
else
|
|
105
|
-
brain="IDX
|
|
96
|
+
brain="IDX:--%"
|
|
106
97
|
fi
|
|
107
98
|
|
|
108
99
|
# ────────────────────────────────────────────────────────
|
package/package.json
CHANGED