@niroai/niro 0.3.4 → 0.3.6

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@niroai/niro",
3
- "version": "0.3.4",
3
+ "version": "0.3.6",
4
4
  "description": "Niro — one package: the niro CLI plus the Niro MCP server (code intelligence for AI coding assistants).",
5
5
  "keywords": [
6
6
  "niro",
package/src/mcp/setup.js CHANGED
@@ -41,17 +41,27 @@ const NIRO_MD_MARKER = "<!-- niro-mcp -->";
41
41
  * own notes added below the block survive uninstall — see teardown.js removeMarkerSection).
42
42
  * Each begins with a leading "\n" because install does `existing + section`.
43
43
  */
44
+ /**
45
+ * Explore-first rule, shared by every client's markdown section. Prose alone is weak, but this is the
46
+ * one lever some clients (Codex, Copilot CLI, Factory, OpenCode, Windsurf) have — they support no hooks,
47
+ * so their ONLY always-on nudge is this file. For Claude Code it reinforces the PreToolUse Grep|Glob|Read
48
+ * hook (NIRO_EXPLORE_COMMAND), which fires once per session/repo/branch; this stays in force for the rest
49
+ * of the session. Wording is imperative and names concrete tools so the agent knows what to call instead
50
+ * of native search.
51
+ */
52
+ const NIRO_EXPLORE_FIRST_SENTENCE = "MUST FOLLOW: before you run grep, find, glob, or open a file to hunt for something, first check whether a Niro tool answers it (code_grep for content search, resolve_fqn and get_source_code for a known symbol, find_handler and find_api_endpoints for entry points). Use the connection tools (get_call_chain, get_reverse_call_chain, get_blast_radius, get_impact_analysis, get_service_topology) to understand how code connects BEFORE deciding which file to read or search next, rather than reading files to rebuild that picture by hand. Native search is the fallback for what Niro does not cover (config files, docs, logs, generated output, non-code text) or when the tools are unavailable.";
53
+
44
54
  const NIRO_MULTI_REPO_SENTENCE = "The temporary project answer is per (repo, BRANCH): after you create/switch a branch in a repo — and before your first edit on that branch — call should_create_temp_project with the repo's git url, its CURRENT branch, and its `.niro-project` id if present, then run `niro new-temp-project` in its folder if needs_temp_project is true. Re-check after every branch change (a `false` from another branch does NOT carry over); this applies to every repo you touch, including when the session started in a parent folder that is not itself a git repo.";
45
- const NIRO_CLAUDE_MD_SECTION = `\n${NIRO_MD_MARKER}\n## Niro MCP\nNiro MCP tools are available for code intelligence. Use them (call chains, blast radius, impact analysis, source lookup) before falling back to native grep/read for code questions.\nBefore adding/writing any new code, you MUST use the find_reusable_code tool from niro to figure out if there is anything reusable.\n${NIRO_MULTI_REPO_SENTENCE}\n`;
46
- const NIRO_CODEX_MD_SECTION = `\n${NIRO_MD_MARKER}\n## Niro MCP\nNiro MCP code-intelligence tools and the \`niro-skill\` are available (call chains, blast radius, impact analysis, source lookup). Prefer them over grep/read for questions about this codebase; the niro-skill has the full tool-routing playbook. Before adding or writing any new code, use find_reusable_code to check for reusable code. If the niro tools are not present or the repo is not indexed, use native tools.\n${NIRO_MULTI_REPO_SENTENCE}\n`;
47
- const NIRO_WINDSURF_MD_SECTION = `\n${NIRO_MD_MARKER}\n## Niro MCP\nNiro MCP code-intelligence tools and the niro-skill are available. Prefer them over grep/read for questions about this codebase (structure, call chains, blast radius, impact, data flows, reuse); the niro-skill has the full routing playbook. Before writing any new code, use find_reusable_code. If the niro tools are not present or the repo is not indexed, use native tools.\n${NIRO_MULTI_REPO_SENTENCE}\n`;
55
+ const NIRO_CLAUDE_MD_SECTION = `\n${NIRO_MD_MARKER}\n## Niro MCP\nNiro MCP tools are available for code intelligence. Use them (call chains, blast radius, impact analysis, source lookup) before falling back to native grep/read for code questions.\nBefore adding/writing any new code, you MUST use the find_reusable_code tool from niro to figure out if there is anything reusable.\n${NIRO_EXPLORE_FIRST_SENTENCE}\n${NIRO_MULTI_REPO_SENTENCE}\n`;
56
+ const NIRO_CODEX_MD_SECTION = `\n${NIRO_MD_MARKER}\n## Niro MCP\nNiro MCP code-intelligence tools and the \`niro-skill\` are available (call chains, blast radius, impact analysis, source lookup). Prefer them over grep/read for questions about this codebase; the niro-skill has the full tool-routing playbook. Before adding or writing any new code, use find_reusable_code to check for reusable code. If the niro tools are not present or the repo is not indexed, use native tools.\n${NIRO_EXPLORE_FIRST_SENTENCE}\n${NIRO_MULTI_REPO_SENTENCE}\n`;
57
+ const NIRO_WINDSURF_MD_SECTION = `\n${NIRO_MD_MARKER}\n## Niro MCP\nNiro MCP code-intelligence tools and the niro-skill are available. Prefer them over grep/read for questions about this codebase (structure, call chains, blast radius, impact, data flows, reuse); the niro-skill has the full routing playbook. Before writing any new code, use find_reusable_code. If the niro tools are not present or the repo is not indexed, use native tools.\n${NIRO_EXPLORE_FIRST_SENTENCE}\n${NIRO_MULTI_REPO_SENTENCE}\n`;
48
58
  /**
49
59
  * Shared generic section for clients whose "always-on instructions" file has no companion
50
60
  * skill mechanism we've verified (Gemini CLI's GEMINI.md, GitHub Copilot CLI's
51
61
  * copilot-instructions.md, Factory's AGENTS.md, OpenCode's AGENTS.md) — one wording, reused
52
62
  * across those destination files (teardown matches it per-file, so reuse is safe).
53
63
  */
54
- const NIRO_GENERIC_MD_SECTION = `\n${NIRO_MD_MARKER}\n## Niro MCP\nNiro MCP code-intelligence tools are available (call chains, blast radius, impact analysis, source lookup). Prefer them over grep/read for questions about this codebase. Before adding or writing any new code, use find_reusable_code to check for reusable code. If the niro tools are not present or the repo is not indexed, use native tools.\n${NIRO_MULTI_REPO_SENTENCE}\n`;
64
+ const NIRO_GENERIC_MD_SECTION = `\n${NIRO_MD_MARKER}\n## Niro MCP\nNiro MCP code-intelligence tools are available (call chains, blast radius, impact analysis, source lookup). Prefer them over grep/read for questions about this codebase. Before adding or writing any new code, use find_reusable_code to check for reusable code. If the niro tools are not present or the repo is not indexed, use native tools.\n${NIRO_EXPLORE_FIRST_SENTENCE}\n${NIRO_MULTI_REPO_SENTENCE}\n`;
55
65
 
56
66
  /** All niro markdown sections — the uninstaller tries each for an exact removal. */
57
67
  const NIRO_MD_SECTIONS = [NIRO_CLAUDE_MD_SECTION, NIRO_CODEX_MD_SECTION, NIRO_WINDSURF_MD_SECTION, NIRO_GENERIC_MD_SECTION];
@@ -116,6 +126,18 @@ function isNiroPreToolUseHook(h) {
116
126
  return cmds.some((c) => c.includes("should_create_temp_project") || c.includes("should_sandbox_repo"));
117
127
  }
118
128
 
129
+ /**
130
+ * True if `h` is niro's PreToolUse explore-first reminder entry (matcher Grep|Glob|Read). Keyed on the
131
+ * unique `niro-explore-` marker-file prefix so a foreign PreToolUse hook on the same matcher is never
132
+ * touched. Shared by installer (upgrade-in-place) and uninstaller (removal).
133
+ */
134
+ function isNiroExploreHook(h) {
135
+ const cmds = [];
136
+ if (h && Array.isArray(h.hooks)) h.hooks.forEach((i) => i && typeof i.command === "string" && cmds.push(i.command));
137
+ if (h && typeof h.command === "string") cmds.push(h.command);
138
+ return cmds.some((c) => c.includes("niro-explore-"));
139
+ }
140
+
119
141
  /**
120
142
  * Writes a niro-managed skill/rule file, refreshing it when the generated content has
121
143
  * changed. These files are GENERATED (not hand-edited), so refresh-on-change is correct:
@@ -541,6 +563,7 @@ async function setupClaudeCode(apiKey, mcpServerUrl) {
541
563
  addClaudeCodePostCompactHook();
542
564
  addClaudeCodeUserPromptHook();
543
565
  addClaudeCodePreToolUseHook();
566
+ addClaudeCodeExploreHook();
544
567
  addClaudeCodeMemory();
545
568
  addClaudeCodeSkill();
546
569
  addClaudeCodeCliSkill();
@@ -777,6 +800,88 @@ function addClaudeCodePreToolUseHook() {
777
800
  }
778
801
  }
779
802
 
803
+ /**
804
+ * The niro PreToolUse hook command (matcher Grep|Glob|Read). Prose instructions (CLAUDE.md, the
805
+ * UserPromptSubmit hook, the niro skill) tell Claude to prefer niro tools for code exploration, but in
806
+ * practice that only sticks when a task is phrased as a discovery question — a task phrased as "fix X" or
807
+ * "add Y" skips straight to native search with no interruption. This hook is the deterministic backstop:
808
+ * once per (session, repo, branch), the first native Grep/Glob/Read call in a niro-indexed repo gets an
809
+ * advisory reminder to prefer niro's structural tools instead — regardless of how the task was phrased.
810
+ *
811
+ * Dependency-free (no jq): reads the PreToolUse JSON on stdin. Read supplies `file_path`; Grep/Glob supply
812
+ * `path` (often omitted, meaning cwd) — so this tries file_path, then path, then the hook's own `cwd`
813
+ * field, then falls back to $PWD. Walks up for a `.niro-project` marker (silent outside indexed repos)
814
+ * and dedupes via a marker file keyed by session_id + repo root + branch, same scheme as the Edit|Write
815
+ * temp-project hook but under the distinct `niro-explore-` prefix so the two hooks never collide.
816
+ */
817
+ const NIRO_EXPLORE_COMMAND = [
818
+ 'IN="$(cat)"',
819
+ // Read gives file_path; Grep/Glob give path (may be absent); fall back to cwd, then $PWD.
820
+ `f="$(printf '%s' "$IN" | grep -oE '"file_path"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 | sed -E 's/.*:[[:space:]]*"([^"]*)"/\\1/')"`,
821
+ `[ -z "$f" ] && f="$(printf '%s' "$IN" | grep -oE '"path"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 | sed -E 's/.*:[[:space:]]*"([^"]*)"/\\1/')"`,
822
+ `[ -z "$f" ] && f="$(printf '%s' "$IN" | grep -oE '"cwd"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 | sed -E 's/.*:[[:space:]]*"([^"]*)"/\\1/')"`,
823
+ '[ -z "$f" ] && f="$PWD"',
824
+ 'd0="$f"; [ -d "$d0" ] || d0="$(dirname "$f")"',
825
+ 'r="$(git -C "$d0" rev-parse --show-toplevel 2>/dev/null)"',
826
+ '[ -z "$r" ] && exit 0',
827
+ // only remind inside a niro-indexed tree
828
+ 'd="$r"; ok=0; while [ -n "$d" ] && [ "$d" != "/" ]; do if [ -f "$d/.niro-project" ]; then ok=1; break; fi; d="$(dirname "$d")"; done',
829
+ '[ "$ok" = 0 ] && exit 0',
830
+ `s="$(printf '%s' "$IN" | grep -oE '"session_id"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 | sed -E 's/.*:[[:space:]]*"([^"]*)"/\\1/')"`,
831
+ // Branch-aware dedup, same rationale as the Edit|Write hook: a stale answer from a different branch must not
832
+ // silence the reminder after a branch switch.
833
+ 'b="$(git -C "$r" rev-parse --abbrev-ref HEAD 2>/dev/null)"',
834
+ `k="$(printf '%s\\n%s\\n%s' "$s" "$r" "$b" | cksum | cut -d' ' -f1)"`,
835
+ 'm="${TMPDIR:-/tmp}/niro-explore-$k"',
836
+ '[ -f "$m" ] && exit 0',
837
+ 'touch "$m"',
838
+ // Advisory context injection ONLY — no permissionDecision, so it never blocks the read/search.
839
+ `printf '%s' '{"hookSpecificOutput":{"hookEventName":"PreToolUse","additionalContext":"This is a niro-indexed repo. For code structure/behavior/call-chain/impact/reuse questions - including ones that come up mid-task while implementing or fixing something, not just standalone discovery questions - prefer niro MCP tools (get_source_code, code_grep, get_call_chain, get_blast_radius, get_impact_analysis, find_reusable_code, get_class_structure, etc.) over native Grep/Glob/Read. Fall back to native tools silently for out-of-scope questions (config files, docs, runtime state, non-code content). Shown once per repo per branch per session."}}'`,
840
+ ].join('; ');
841
+
842
+ /**
843
+ * Adds the PreToolUse (Grep|Glob|Read) explore-first reminder hook to ~/.claude/settings.json. Idempotent:
844
+ * upgrades a stale niro entry in place, never touches a foreign PreToolUse hook on the same matcher
845
+ * (matched by the niro-explore- marker via isNiroExploreHook).
846
+ */
847
+ function addClaudeCodeExploreHook() {
848
+ const settingsPath = path.join(process.env.HOME || "", ".claude", "settings.json");
849
+ let settings = {};
850
+ try {
851
+ if (fs.existsSync(settingsPath)) settings = JSON.parse(fs.readFileSync(settingsPath, "utf-8"));
852
+ } catch {
853
+ // Corrupt or missing — start fresh
854
+ }
855
+
856
+ if (!settings.hooks) settings.hooks = {};
857
+ if (!settings.hooks.PreToolUse) settings.hooks.PreToolUse = [];
858
+
859
+ const entry = { matcher: "Grep|Glob|Read", hooks: [{ type: "command", command: NIRO_EXPLORE_COMMAND }] };
860
+ const alreadyCurrent = settings.hooks.PreToolUse.some(
861
+ (h) => Array.isArray(h.hooks) && h.hooks.some((i) => i && i.command === NIRO_EXPLORE_COMMAND),
862
+ );
863
+ if (alreadyCurrent) {
864
+ console.log(" PreToolUse explore-first hook already up to date — skipping.\n");
865
+ return;
866
+ }
867
+
868
+ const hadStale = settings.hooks.PreToolUse.some(isNiroExploreHook);
869
+ settings.hooks.PreToolUse = settings.hooks.PreToolUse.filter((h) => !isNiroExploreHook(h));
870
+ settings.hooks.PreToolUse.push(entry);
871
+
872
+ try {
873
+ fs.mkdirSync(path.dirname(settingsPath), { recursive: true });
874
+ fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
875
+ console.log(` PreToolUse explore-first hook ${hadStale ? "upgraded" : "added"} in ~/.claude/settings.json`);
876
+ console.log(" → Before the first native Grep/Glob/Read in a niro-indexed repo, Claude is reminded once/session/branch");
877
+ console.log(" to prefer niro tools — even mid-task, not just for standalone discovery questions.\n");
878
+ } catch (err) {
879
+ console.warn(` Could not write ~/.claude/settings.json: ${err.message}`);
880
+ console.warn(" Add manually to the PreToolUse array in ~/.claude/settings.json:\n");
881
+ console.warn(JSON.stringify(entry, null, 2) + "\n");
882
+ }
883
+ }
884
+
780
885
  /** Body of the `niro` skill (long-form tool-routing playbook). */
781
886
  const NIRO_SKILL = `---
782
887
  name: niro-skill
@@ -791,6 +896,16 @@ code — structure, behavior, impact, data/event flows, semantics — prefer the
791
896
  native grep/read. For anything outside the indexed code (config files, docs, runtime state),
792
897
  use your native tools.
793
898
 
899
+ ## Explore first — MUST FOLLOW
900
+
901
+ Before you run grep, find, glob, or open a file to hunt for something, check whether a Niro
902
+ tool answers it: \`code_grep\` for content search, \`resolve_fqn\` + \`get_source_code\` for a known
903
+ symbol, \`find_handler\` / \`find_api_endpoints\` for entry points. Use the connection tools
904
+ (\`get_call_chain\`, \`get_reverse_call_chain\`, \`get_blast_radius\`, \`get_impact_analysis\`,
905
+ \`get_service_topology\`) to understand how the code connects BEFORE you decide which file to
906
+ read or search next, instead of reading files to rebuild that picture by hand. Native search
907
+ stays the fallback for what Niro does not cover, and when the tools are unavailable.
908
+
794
909
  \`project_id\` auto-resolves from a \`.niro-project\` file at the repo root — omit it. If there
795
910
  is no such file, call \`list_projects\` and pass the chosen \`project_id\`.
796
911
 
@@ -908,6 +1023,7 @@ temporary index of your branch (\`niro new-temp-project\` to create, \`niro disc
908
1023
  | Full path from A to B (prefer \`to_service\` for cross-service) | \`trace_execution_path\` |
909
1024
  | HTTP endpoints a service exposes | \`find_api_endpoints\` |
910
1025
  | Outbound HTTP calls a service makes | \`find_outbound_http_calls\` |
1026
+ | Service-to-service call map (who calls whom), as a Mermaid diagram | \`get_service_topology\` |
911
1027
  | What handles an outgoing HTTP call / event | \`find_handler\` |
912
1028
 
913
1029
  **Change safely**
@@ -1191,6 +1307,13 @@ alwaysApply: false
1191
1307
  The niro MCP server exposes code-intelligence tools for this codebase. Prefer them over
1192
1308
  grep/read for any question answerable from the indexed code.
1193
1309
 
1310
+ MUST FOLLOW — explore first: before you run grep, find, glob, or open a file to hunt for
1311
+ something, check whether a niro tool answers it (code_grep, resolve_fqn + get_source_code,
1312
+ find_handler, find_api_endpoints). Use the connection tools (get_call_chain,
1313
+ get_reverse_call_chain, get_blast_radius, get_impact_analysis, get_service_topology) to see
1314
+ how the code connects BEFORE deciding which file to read or search next. Native search is the
1315
+ fallback for what niro does not cover, and when the tools are unavailable.
1316
+
1194
1317
  Availability self-check FIRST — if the niro tools are not present in this session, or the
1195
1318
  repo is not indexed (no project resolves / a tool reports "not indexed"), fall back to
1196
1319
  native tools silently and do not mention Niro.
@@ -1207,7 +1330,7 @@ Routing (most specific wins):
1207
1330
  <!-- how_does_x_work temporarily disabled (hidden MCP tool) — re-add "how_does_x_work (forward flow) | " to the understand line below when re-enabled -->
1208
1331
  - understand: explain_code (AI docs) | get_source_code | get_class_structure
1209
1332
  - navigate: get_call_chain (callees) | get_reverse_call_chain (callers) | trace_execution_path (A to B)
1210
- - HTTP: find_api_endpoints | find_outbound_http_calls | find_handler
1333
+ - HTTP: find_api_endpoints | find_outbound_http_calls | find_handler | get_service_topology (service-to-service map, Mermaid)
1211
1334
  <!-- why_could_x_happen temporarily disabled (hidden MCP tool) — re-add "why_could_x_happen | " to the debug line below when re-enabled -->
1212
1335
  - debug: find_known_issues | get_state_at_entry
1213
1336
  - data & events: get_data_resource_usage | get_event_producers_and_handlers
@@ -1478,6 +1601,13 @@ Niro code-intelligence MCP tools are available for the current codebase. Prefer
1478
1601
  grep/read for questions about structure, call chains, blast radius, impact, data flows, and
1479
1602
  reuse. Before writing or adding any new code, use \`find_reusable_code\` first.
1480
1603
 
1604
+ MUST FOLLOW — explore first: before you run grep, find, glob, or open a file to hunt for
1605
+ something, check whether a niro tool answers it (\`code_grep\`, \`resolve_fqn\` +
1606
+ \`get_source_code\`, \`find_handler\`, \`find_api_endpoints\`). Use the connection tools
1607
+ (\`get_call_chain\`, \`get_reverse_call_chain\`, \`get_blast_radius\`, \`get_impact_analysis\`,
1608
+ \`get_service_topology\`) to see how the code connects BEFORE deciding which file to read or
1609
+ search next. Native search is the fallback for what niro does not cover.
1610
+
1481
1611
  If the niro tools are not present in this session, or the repo is not indexed (no project
1482
1612
  resolves), fall back to native tools silently — do not mention Niro's absence.
1483
1613
  `;
@@ -1516,6 +1646,13 @@ Niro code-intelligence MCP tools are available for the current codebase. Prefer
1516
1646
  grep/read for questions about structure, call chains, blast radius, impact, data flows, and
1517
1647
  reuse. Before writing or adding any new code, use \`find_reusable_code\` first.
1518
1648
 
1649
+ MUST FOLLOW — explore first: before you run grep, find, glob, or open a file to hunt for
1650
+ something, check whether a niro tool answers it (\`code_grep\`, \`resolve_fqn\` +
1651
+ \`get_source_code\`, \`find_handler\`, \`find_api_endpoints\`). Use the connection tools
1652
+ (\`get_call_chain\`, \`get_reverse_call_chain\`, \`get_blast_radius\`, \`get_impact_analysis\`,
1653
+ \`get_service_topology\`) to see how the code connects BEFORE deciding which file to read or
1654
+ search next. Native search is the fallback for what niro does not cover.
1655
+
1519
1656
  If the niro tools are not present in this session, or the repo is not indexed (no project
1520
1657
  resolves), fall back to native tools silently — do not mention Niro's absence.
1521
1658
  `;
@@ -1957,10 +2094,17 @@ module.exports = {
1957
2094
  isNiroPostCompactHook,
1958
2095
  isNiroUserPromptHook,
1959
2096
  isNiroPreToolUseHook,
2097
+ isNiroExploreHook,
1960
2098
  userHome,
1961
2099
  // Exposed for content regression tests (e.g. the cross-project routing guidance).
1962
2100
  NIRO_SKILL,
1963
2101
  NIRO_CLI_SKILL,
2102
+ // Exposed for tests: the explore-first rule must reach EVERY client's always-on instruction surface.
2103
+ NIRO_EXPLORE_FIRST_SENTENCE,
2104
+ NIRO_CURSOR_RULE,
2105
+ NIRO_CLINE_RULE,
2106
+ NIRO_KIRO_STEERING,
2107
+ upsertNiroMarkerSection,
1964
2108
  // Exposed for tests: URL resolution replaced the install-time prompt.
1965
2109
  resolveMcpServerUrl,
1966
2110
  SAAS_URL,
@@ -5,7 +5,8 @@
5
5
  *
6
6
  * `niro mcp uninstall` removes everything `niro mcp install` wrote, per editor:
7
7
  * - the MCP server registration (claude/codex CLI, or the editor's JSON config)
8
- * - the niro hooks in ~/.claude/settings.json (PostCompact + UserPromptSubmit + PreToolUse)
8
+ * - the niro hooks in ~/.claude/settings.json (PostCompact + UserPromptSubmit + PreToolUse: both the
9
+ * Edit|Write temp-project reminder and the Grep|Glob|Read explore-first reminder)
9
10
  * - the niro section in markdown config (CLAUDE.md / AGENTS.md / Windsurf rules)
10
11
  * - the generated skills (~/.claude/skills, the shared ~/.agents/skills)
11
12
  * - Cursor's "niro:*" allowlist entry and project rule
@@ -38,6 +39,7 @@ const {
38
39
  isNiroPostCompactHook,
39
40
  isNiroUserPromptHook,
40
41
  isNiroPreToolUseHook,
42
+ isNiroExploreHook,
41
43
  userHome,
42
44
  } = require("./setup");
43
45
 
@@ -122,10 +124,10 @@ function removeMarkerSectionBounded(content, marker) {
122
124
  }
123
125
 
124
126
  /**
125
- * Strip niro's PostCompact and UserPromptSubmit hook entries from a parsed Claude Code
126
- * settings object (mutates and returns it). Uses the same matchers install uses. Empty
127
- * arrays / an empty `hooks` object are pruned so the file stays tidy. Other hooks are
128
- * untouched. Returns { settings, removed } where `removed` is the count deleted.
127
+ * Strip niro's PostCompact, UserPromptSubmit, and PreToolUse (temp-project + explore-first) hook
128
+ * entries from a parsed Claude Code settings object (mutates and returns it). Uses the same matchers
129
+ * install uses. Empty arrays / an empty `hooks` object are pruned so the file stays tidy. Other hooks
130
+ * are untouched. Returns { settings, removed } where `removed` is the count deleted.
129
131
  */
130
132
  function filterNiroHooks(settings) {
131
133
  let removed = 0;
@@ -148,7 +150,7 @@ function filterNiroHooks(settings) {
148
150
  }
149
151
  if (Array.isArray(hooks.PreToolUse)) {
150
152
  const before = hooks.PreToolUse.length;
151
- hooks.PreToolUse = hooks.PreToolUse.filter((h) => !isNiroPreToolUseHook(h));
153
+ hooks.PreToolUse = hooks.PreToolUse.filter((h) => !isNiroPreToolUseHook(h) && !isNiroExploreHook(h));
152
154
  removed += before - hooks.PreToolUse.length;
153
155
  if (hooks.PreToolUse.length === 0) delete hooks.PreToolUse;
154
156
  }