projecta-rrr 1.22.2 → 1.22.4

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,49 @@ 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.22.4] - 2026-04-19
8
+
9
+ **Patch: semantic search wired into 5 agents; rrr-explore tool names fixed.**
10
+
11
+ ### Fixed
12
+ - **`rrr-explore` broken tool names** — was referencing `mcp:semantic_search` / `mcp:search_sessions` (unresolvable shorthand), replaced with real tool names `mcp__rrr-search-hosted__semantic_search` and `mcp__rrr-search-hosted__search_sessions` throughout frontmatter and all body examples. Semantic search was silently falling back to grep/bash on every invocation.
13
+
14
+ ### Added
15
+ - **Semantic search added to 4 agents** that previously had none:
16
+ - `rrr-verifier` — finds feature implementations for goal-backward verification
17
+ - `rrr-integration-checker` — finds cross-phase connection points
18
+ - `rrr-project-researcher` — adds codebase search to supplement web/Context7 research; also gets `search_sessions`
19
+ - `rrr-roadmapper` — useful for brownfield codebase mapping
20
+
21
+ ---
22
+
23
+ ## [1.22.3] - 2026-04-19
24
+
25
+ **Patch: postinstall full provision + tool-disallow real-payload subagent detection + hook wiring.**
26
+
27
+ ### Fixed
28
+ - **`bin/install.js` postinstall now full-provisions** (was `--hud-only`). Fresh `npm install -g projecta-rrr` lands 47 commands, 13 agents, 18 hooks, plus settings.json with v1.22 hooks pre-wired (default-safe). Previously users had to run `npx projecta-rrr install` separately to get commands/agents.
29
+ - **v1.22 hooks now auto-wired** into `~/.claude/settings.json` on install:
30
+ - `PreToolUse` matcher `Task|Agent` → `hooks/model-router.js`
31
+ - `PreToolUse` matcher `Read|Grep|Glob` → `hooks/tool-disallow.js`
32
+ - `PostToolUse` matcher `Edit|MultiEdit` → `hooks/edit-batching-nudge.js`
33
+ - Defaults seeded: `settings.rrr.model_router: "static"` (v1.21 behavior), `settings.rrr.tok_disallow: false` (opt-in).
34
+ - Idempotent — re-install does not duplicate entries.
35
+ - Previously hook files copied but not wired (only `runHostedInstallOrchestrator()` wired them, which only runs with `--enable-hosted`).
36
+ - **`hooks/tool-disallow.js` subagent detection** now uses real Claude Code payload schema. Previous heuristic checked `input.agent_name` which does NOT exist in PreToolUse payloads (UAT-verified schema: `{session_id, transcript_path, cwd, permission_mode, hook_event_name, tool_name, tool_input, tool_use_id}`). New mechanism: read tail of `transcript_path` and check `isSidechain: true` to detect subagent context. Fallback: legacy `agent_name` field for forward-compat. Fail-CLOSED if transcript unreadable (over-block better than under-block when goal is token discipline).
37
+
38
+ ### Operator data populated this session
39
+ - **`installations` table**: 5 rows (was 0) — populated from existing `repos.installation_id` data.
40
+ - **`repos.github_repo_id` backfill**: 24 of 59 real repos populated via `gh api /repos/PA-Ai-Team/<slug>`. Remaining 35 are inaccessible to the user PAT (likely deleted, archived, or upstream forks like OpenHands/letta — webhook routing for them is unreachable anyway, so NULL is correct).
41
+
42
+ ### Verified in UAT
43
+ - Fresh `HOME=tmp npm install -g projecta-rrr@1.22.3 --prefix=tmp` lands 47 commands + 13 agents + 18 hooks + 3 wired hook entries with safe defaults. Re-install is idempotent (no duplicate entries).
44
+ - `tool-disallow`: main-thread Read with synthetic transcript (`isSidechain:false`) → BLOCK exit 2; subagent Read with `isSidechain:true` → ALLOW exit 0.
45
+ - 239/239 tests pass; `prepublish:check` clean.
46
+
47
+ ### Known followups
48
+ - 35 repos with NULL `github_repo_id` (private/deleted) — operator can run via App-installation token (broader scope) if needed; for now webhook safely skips them with `{"skipped":"unindexed-repo"}`.
49
+
7
50
  ## [1.22.2] - 2026-04-19
8
51
 
9
52
  **Patch: dynamic routing now actually fires in Claude Code (Opus 4.7+).**
@@ -1,8 +1,8 @@
1
1
  ---
2
2
  name: rrr-explore
3
- description: Code exploration agent. Semantic-first via mcp:semantic_search; narrow Bash for exact identifiers only.
3
+ description: Code exploration agent. Semantic-first via mcp__rrr-search-hosted__semantic_search; narrow Bash for exact identifiers only.
4
4
  model: haiku
5
- tools: Bash, mcp:semantic_search, mcp:search_sessions
5
+ tools: Bash, mcp__rrr-search-hosted__semantic_search, mcp__rrr-search__semantic_search, mcp__rrr-search-hosted__search_sessions, mcp__rrr-search__search_sessions
6
6
  disallowedTools: [Read, Grep, Glob]
7
7
  bashAllowList:
8
8
  - "rg"
@@ -23,7 +23,7 @@ You are a RRR code exploration agent. You explore codebases using intelligent se
23
23
 
24
24
  **No narration:** Do NOT emit text between tool calls. Think, call tools, return structured findings at the end. Inter-call prose burns tokens and degrades Haiku output quality.
25
25
 
26
- **Read/Grep/Glob are disabled for you.** Use `mcp:semantic_search` for concepts, `mcp:search_sessions` for decision history, and narrow Bash (`rg`, `git log/show/blame`, `ls`, `wc` only) for exact-identifier fallback. If you genuinely need to read a whole file, return a `needs_read: <path>` hint and let the caller do it.
26
+ **Read/Grep/Glob are disabled for you.** Use `mcp__rrr-search-hosted__semantic_search` for concepts, `mcp__rrr-search-hosted__search_sessions` for decision history, and narrow Bash (`rg`, `git log/show/blame`, `ls`, `wc` only) for exact-identifier fallback. If you genuinely need to read a whole file, return a `needs_read: <path>` hint and let the caller do it.
27
27
 
28
28
  **Core Responsibilities:**
29
29
 
@@ -104,7 +104,7 @@ Start with questions 1 and 2. Only use question 3 when you have specific identif
104
104
 
105
105
  **Invocation:**
106
106
  ```
107
- mcp:semantic_search query="authentication flow" limit=10
107
+ mcp__rrr-search-hosted__semantic_search query="authentication flow" limit=10
108
108
  ```
109
109
 
110
110
  ### Use search_sessions (for historical context):
@@ -117,7 +117,7 @@ mcp:semantic_search query="authentication flow" limit=10
117
117
 
118
118
  **Invocation:**
119
119
  ```
120
- mcp:search_sessions query="why PostgreSQL over MongoDB" limit=5
120
+ mcp__rrr-search-hosted__search_sessions query="why PostgreSQL over MongoDB" limit=5
121
121
  ```
122
122
 
123
123
  ### Use Bash (`rg` only) for exact identifiers:
@@ -243,12 +243,12 @@ Use search_sessions for understanding past decisions:
243
243
  User: "Why did we use JWT instead of sessions?"
244
244
 
245
245
  1. Query session history:
246
- mcp:search_sessions query="JWT vs sessions authentication decision"
246
+ mcp__rrr-search-hosted__search_sessions query="JWT vs sessions authentication decision"
247
247
 
248
248
  2. Review results for decision context
249
249
 
250
250
  3. If needed, supplement with semantic_search for current implementation:
251
- mcp:semantic_search query="JWT authentication implementation"
251
+ mcp__rrr-search-hosted__semantic_search query="JWT authentication implementation"
252
252
  ```
253
253
 
254
254
  **Typical usage:**
@@ -271,12 +271,12 @@ Before searching, clarify:
271
271
 
272
272
  **For conceptual code questions (most common):**
273
273
  ```
274
- mcp:semantic_search query="how user authentication works" limit=10
274
+ mcp__rrr-search-hosted__semantic_search query="how user authentication works" limit=10
275
275
  ```
276
276
 
277
277
  **For past decisions and context:**
278
278
  ```
279
- mcp:search_sessions query="why we chose JWT over sessions" limit=5
279
+ mcp__rrr-search-hosted__search_sessions query="why we chose JWT over sessions" limit=5
280
280
  ```
281
281
 
282
282
  **For exact identifiers (specific lookup):**
@@ -2,7 +2,7 @@
2
2
  name: rrr-integration-checker
3
3
  description: Verifies cross-phase integration and E2E flows. Checks that phases connect properly and user workflows complete end-to-end.
4
4
  model: haiku
5
- tools: Read, Bash, Grep, Glob
5
+ tools: Read, Bash, Grep, Glob, mcp__rrr-search-hosted__semantic_search, mcp__rrr-search__semantic_search
6
6
  color: blue
7
7
  ---
8
8
 
@@ -2,7 +2,7 @@
2
2
  name: rrr-project-researcher
3
3
  description: Researches domain ecosystem. Supports two modes - project research (.planning/research/) and milestone research (.planning/milestones/vX.Y/research/). Milestone research inherits from project research.
4
4
  model: sonnet
5
- tools: Read, Write, Bash, Grep, Glob, WebSearch, WebFetch, mcp__context7__*
5
+ tools: Read, Write, Bash, Grep, Glob, WebSearch, WebFetch, mcp__context7__*, mcp__rrr-search-hosted__semantic_search, mcp__rrr-search__semantic_search, mcp__rrr-search-hosted__search_sessions, mcp__rrr-search__search_sessions
6
6
  color: cyan
7
7
  ---
8
8
 
@@ -2,7 +2,7 @@
2
2
  name: rrr-roadmapper
3
3
  description: Creates project roadmaps with phase breakdown, requirement mapping, success criteria derivation, and coverage validation. Spawned by /rrr:new-project orchestrator.
4
4
  model: sonnet
5
- tools: Read, Write, Bash, Glob, Grep
5
+ tools: Read, Write, Bash, Glob, Grep, mcp__rrr-search-hosted__semantic_search, mcp__rrr-search__semantic_search
6
6
  color: purple
7
7
  ---
8
8
 
@@ -2,7 +2,7 @@
2
2
  name: rrr-verifier
3
3
  description: Verifies phase goal achievement through goal-backward analysis. Checks codebase delivers what phase promised, not just that tasks completed. Creates VERIFICATION.md report.
4
4
  model: sonnet
5
- tools: Read, Bash, Grep, Glob
5
+ tools: Read, Bash, Grep, Glob, mcp__rrr-search-hosted__semantic_search, mcp__rrr-search__semantic_search
6
6
  color: green
7
7
  ---
8
8
 
package/bin/install.js CHANGED
@@ -1730,6 +1730,47 @@ function install(isGlobal) {
1730
1730
  console.log(` ${green}✓${reset} Installed hooks`);
1731
1731
  }
1732
1732
 
1733
+ // v1.22.3: wire Phase 83 (model-router), Phase 85 (tool-disallow + edit-batching-nudge)
1734
+ // PreToolUse/PostToolUse hooks. Idempotent — checks for existing entries before
1735
+ // adding. Defaults are SAFE (settings.rrr.tok_disallow=false; model_router=static).
1736
+ // Previously these were only wired when --enable-hosted ran the hosted orchestrator,
1737
+ // so users got the hooks on disk but no settings.json registration.
1738
+ if (bashStatus.available && fs.existsSync(path.join(claudeDir, 'hooks', 'model-router.js'))) {
1739
+ settings.hooks = settings.hooks || {};
1740
+ settings.hooks.PreToolUse = settings.hooks.PreToolUse || [];
1741
+ settings.hooks.PostToolUse = settings.hooks.PostToolUse || [];
1742
+ settings.rrr = settings.rrr || {};
1743
+
1744
+ const wireOnce = (chain, matcher, hookCmd, label) => {
1745
+ const exists = chain.some((entry) =>
1746
+ entry && Array.isArray(entry.hooks) && entry.hooks.some(
1747
+ (h) => h && typeof h.command === 'string' && h.command.includes(label)
1748
+ )
1749
+ );
1750
+ if (!exists) {
1751
+ chain.push({ matcher, hooks: [{ type: 'command', command: hookCmd }] });
1752
+ return true;
1753
+ }
1754
+ return false;
1755
+ };
1756
+
1757
+ let wiredCount = 0;
1758
+ if (wireOnce(settings.hooks.PreToolUse, 'Task|Agent', '$HOME/.claude/hooks/model-router.js', 'model-router.js')) wiredCount++;
1759
+ if (fs.existsSync(path.join(claudeDir, 'hooks', 'tool-disallow.js'))) {
1760
+ if (wireOnce(settings.hooks.PreToolUse, 'Read|Grep|Glob', 'node $HOME/.claude/hooks/tool-disallow.js', 'tool-disallow.js')) wiredCount++;
1761
+ }
1762
+ if (fs.existsSync(path.join(claudeDir, 'hooks', 'edit-batching-nudge.js'))) {
1763
+ if (wireOnce(settings.hooks.PostToolUse, 'Edit|MultiEdit', 'node $HOME/.claude/hooks/edit-batching-nudge.js', 'edit-batching-nudge.js')) wiredCount++;
1764
+ }
1765
+ if (settings.rrr.model_router === undefined) settings.rrr.model_router = 'static';
1766
+ if (settings.rrr.tok_disallow === undefined) settings.rrr.tok_disallow = false;
1767
+ if (wiredCount > 0) {
1768
+ console.log(` ${green}✓${reset} Wired ${wiredCount} v1.22 hook(s) (settings.rrr.model_router=static, tok_disallow=false — opt-in)`);
1769
+ } else {
1770
+ console.log(` ${dim}✓${reset} v1.22 hooks already wired`);
1771
+ }
1772
+ }
1773
+
1733
1774
  const statuslineCommand = isGlobal
1734
1775
  ? '$HOME/.claude/hooks/statusline.sh'
1735
1776
  : `${localDirName}/hooks/statusline.sh`;
@@ -18,9 +18,16 @@
18
18
  * and scripts/test-install-smoke.js).
19
19
  * - env RRR_TOK_DISALLOW=off → kill-switch override.
20
20
  *
21
- * Subagent exemption:
22
- * - PreToolUse JSON payload includes `agent_name` for subagent calls.
23
- * Empty/missing = main thread.
21
+ * Subagent exemption (v1.22.3 fix — UAT-verified payload schema):
22
+ * - Real Claude Code PreToolUse payload does NOT include `agent_name`.
23
+ * Schema is: {session_id, transcript_path, cwd, permission_mode,
24
+ * hook_event_name, tool_name, tool_input, tool_use_id}
25
+ * - Subagent context is detected by reading the LAST line of
26
+ * `transcript_path` and checking `isSidechain: true`. Sidechain entries
27
+ * are subagent calls; main thread is `isSidechain: false`.
28
+ * - Fallback: if transcript can't be read or parsed, treat as main thread
29
+ * (fail-CLOSED for the BLOCK case — better to over-block than under-block
30
+ * when the goal is token discipline).
24
31
  *
25
32
  * Settings location (read-only): ~/.claude/settings.json (then project
26
33
  * .claude/settings.json — project wins). Never modified here; wiring is
@@ -66,6 +73,43 @@ function isAgentExempt(agentName, exemptList) {
66
73
  return true;
67
74
  }
68
75
 
76
+ /**
77
+ * Detect subagent context by reading the tail of the Claude Code transcript
78
+ * and checking for `isSidechain: true` on recent entries.
79
+ *
80
+ * Real Claude Code PreToolUse payload includes `transcript_path` but NOT
81
+ * `agent_name`. The transcript JSONL has one entry per turn with isSidechain
82
+ * marking subagent context (UAT-verified in v1.22.3).
83
+ *
84
+ * Returns: 'subagent' | 'main' | 'unknown'
85
+ */
86
+ function detectThreadFromTranscript(transcriptPath) {
87
+ if (!transcriptPath || typeof transcriptPath !== 'string') return 'unknown';
88
+ try {
89
+ if (!fs.existsSync(transcriptPath)) return 'unknown';
90
+ // Read last ~64KB only (transcripts can be large; we only need recent context).
91
+ const stat = fs.statSync(transcriptPath);
92
+ const fd = fs.openSync(transcriptPath, 'r');
93
+ const tailSize = Math.min(stat.size, 65536);
94
+ const buf = Buffer.alloc(tailSize);
95
+ fs.readSync(fd, buf, 0, tailSize, Math.max(0, stat.size - tailSize));
96
+ fs.closeSync(fd);
97
+ const lines = buf.toString('utf8').split('\n').filter(Boolean);
98
+ // Walk backward — the most recent non-tool-result entry is the current turn.
99
+ for (let i = lines.length - 1; i >= 0; i--) {
100
+ try {
101
+ const e = JSON.parse(lines[i]);
102
+ if (typeof e.isSidechain === 'boolean') {
103
+ return e.isSidechain ? 'subagent' : 'main';
104
+ }
105
+ } catch { /* skip malformed line */ }
106
+ }
107
+ return 'unknown';
108
+ } catch {
109
+ return 'unknown';
110
+ }
111
+ }
112
+
69
113
  function decideMode(settings) {
70
114
  // Env kill-switch wins.
71
115
  if ((process.env.RRR_TOK_DISALLOW || '').toLowerCase() === 'off') return 'off';
@@ -98,12 +142,18 @@ function main() {
98
142
  // a misconfigured settings.json never breaks the installer.
99
143
  if (process.env.RRR_INTERNAL_TOOL === '1') { process.exit(0); }
100
144
 
101
- // Subagent exemption — agent_name set = subagent context.
102
- const agentName = input.agent_name || (input.session && input.session.agent_name) || '';
145
+ // Subagent exemption — v1.22.3: detect via transcript_path + isSidechain.
146
+ // Real Claude Code payload schema does NOT include input.agent_name.
147
+ // Fallback to legacy input.agent_name field for forward-compat with future
148
+ // Claude Code versions that may add it.
149
+ const legacyAgentName = input.agent_name || (input.session && input.session.agent_name) || '';
150
+ const thread = legacyAgentName ? 'subagent' : detectThreadFromTranscript(input.transcript_path);
103
151
  const settings = loadSettings();
104
152
  const exemptList = settings.tok_disallow_exempt;
105
153
 
106
- if (isAgentExempt(agentName, exemptList)) { process.exit(0); }
154
+ // Subagent (sidechain) always exempt. Unknown → treat as main (fail-CLOSED).
155
+ if (thread === 'subagent') { process.exit(0); }
156
+ if (legacyAgentName && isAgentExempt(legacyAgentName, exemptList)) { process.exit(0); }
107
157
 
108
158
  // Explicit per-name allow (rare: when main-thread invocation of a
109
159
  // specific named context should still be permitted).
@@ -127,5 +177,6 @@ module.exports = {
127
177
  BLOCKED_TOOLS,
128
178
  decideMode,
129
179
  isAgentExempt,
180
+ detectThreadFromTranscript,
130
181
  buildMessage,
131
182
  };
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "projecta-rrr",
3
- "version": "1.22.2",
3
+ "version": "1.22.4",
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",
7
7
  "projecta-rrr-hosted-setup": "bin/hosted-setup.js"
8
8
  },
9
9
  "scripts": {
10
- "postinstall": "node bin/install.js --hud-only --global && node scripts/register-mcp.js",
10
+ "postinstall": "node bin/install.js --global --yes --skip-optimization && node scripts/register-mcp.js",
11
11
  "watch": "node watcher/index.js",
12
12
  "watch:verbose": "node watcher/index.js --verbose",
13
13
  "watch:claude-code": "node watcher/watchers/claude-code.js --verbose",