Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
59e6382
docs(specs): add 009 explore tooling enhancement spec kit
w0wl0lxd Aug 1, 2026
2e3cbf7
docs(specs): remediate 009 spec kit after analyze
w0wl0lxd Aug 1, 2026
b2c9888
feat(plugins): implement explore router intents and prompt hierarchy …
w0wl0lxd Aug 1, 2026
c3b8603
test: refine router classification test and add auto-detection patterns
w0wl0lxd Aug 2, 2026
384f4ce
docs: mark Phase 1 baseline tasks as complete
w0wl0lxd Aug 2, 2026
14c875f
feat(codegraph): add callers, callees, impact, affected, node, query,…
w0wl0lxd Aug 2, 2026
33296e9
feat(codegraph): complete Phase 3 with affected database query and Ca…
w0wl0lxd Aug 2, 2026
c167141
feat(bash): harden rtk integration (phase 6)
w0wl0lxd Aug 2, 2026
64cb253
docs: update agent prompts and docs for explore tooling (phase 7)
w0wl0lxd Aug 2, 2026
ed7764f
fix(codegraph): fix clippy warnings from Phase 3 changes
w0wl0lxd Aug 2, 2026
5ee852a
feat(arbor): expand arbor commands and native fallbacks (phase 4)
w0wl0lxd Aug 2, 2026
a15654c
feat(semblem): add upstream semble cli wrapper with bm25 fallback (ph…
w0wl0lxd Aug 2, 2026
964a279
fix: clippy warnings for arbor and semble crates
w0wl0lxd Aug 2, 2026
30db14c
docs: update tasks.md with phase 4-5 completion and phase 8 partial s…
w0wl0lxd Aug 2, 2026
e9e923b
test: final verification and baseline comparison (phase 8)
w0wl0lxd Aug 2, 2026
15f951e
fix(explore): route impact intent to codegraph and tighten auto-detec…
w0wl0lxd Aug 2, 2026
b412ff2
fix(semblem): add tempfile to n00n-semble dependencies
w0wl0lxd Aug 2, 2026
f307008
fix(explore): tighten trace/relations routing and update classifier f…
w0wl0lxd Aug 2, 2026
fec9cf5
test(explore): make plugin spec runnable in n00n-lua runtime
w0wl0lxd Aug 2, 2026
2db9355
style(n00n-lua): clippy cleanups in codegraph bindings
w0wl0lxd Aug 2, 2026
6e8fbb0
fix(n00n-lua): sync semblem docs and clean imports
w0wl0lxd Aug 2, 2026
c06decd
docs(n00n-lua): add missing arbor API docs
w0wl0lxd Aug 2, 2026
f58ec60
style(n00n-semble): collapse nested ifs for clippy
w0wl0lxd Aug 2, 2026
de90ee7
test(explore): add fixture path fallback and use n00n.fs in spec
w0wl0lxd Aug 2, 2026
76dbfe1
fix(explore): address review findings across explore, codegraph, arbo…
w0wl0lxd Aug 2, 2026
4fef11f
fix: address CodeRabbit and Codex review findings
w0wl0lxd Aug 2, 2026
ce5462c
ci: add changelog fragment, regenerate docs, and ignore n00n-redact i…
w0wl0lxd Aug 2, 2026
8961c09
test: update token-profile baseline and tool-definition budgets for e…
w0wl0lxd Aug 2, 2026
f551f2a
Merge branch 'main' into 009-explore-tooling-enhancement
w0wl0lxd Aug 2, 2026
7bd6d0a
fix: address CodeRabbit review findings
w0wl0lxd Aug 2, 2026
6fb41eb
fix: address latest CodeRabbit review findings
w0wl0lxd Aug 2, 2026
6a65b33
build: allow CDLA-Permissive-2.0 license for webpki-roots
w0wl0lxd Aug 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitleaks.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ paths = [
# strings that look like API keys but are not credentials.
'''maki-agent/''',
'''maki-providers/''',
'''n00n-redact/''',
]
regexes = [
# Feature/version date identifiers, e.g. "oauth-2025-04-20" or
Expand Down
2 changes: 1 addition & 1 deletion .specify/feature.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"feature_directory":"specs/007-cursor-native-provider"}
{"feature_directory":"specs/009-explore-tooling-enhancement"}
Comment thread
w0wl0lxd marked this conversation as resolved.
60 changes: 24 additions & 36 deletions .specify/memory/constitution.md
Original file line number Diff line number Diff line change
@@ -1,50 +1,38 @@
# [PROJECT_NAME] Constitution
<!-- Example: Spec Constitution, TaskFlow Constitution, etc. -->
# n00n Constitution

## Core Principles

### [PRINCIPLE_1_NAME]
<!-- Example: I. Library-First -->
[PRINCIPLE_1_DESCRIPTION]
<!-- Example: Every feature starts as a standalone library; Libraries must be self-contained, independently testable, documented; Clear purpose required - no organizational-only libraries -->
### Token-Efficient Exploration

### [PRINCIPLE_2_NAME]
<!-- Example: II. CLI Interface -->
[PRINCIPLE_2_DESCRIPTION]
<!-- Example: Every library exposes functionality via CLI; Text in/out protocol: stdin/args → stdout, errors → stderr; Support JSON + human-readable formats -->
Prefer `codegraph`, `arbor`, `semblem`, `index`, and `rtk` over broad greps and raw shell output; compress before context. Use `explore` as the primary entry point for codebase questions, routing to the appropriate structural backend. Filter and pipeline with `code_execution` to reduce context usage.

### [PRINCIPLE_3_NAME]
<!-- Example: III. Test-First (NON-NEGOTIABLE) -->
[PRINCIPLE_3_DESCRIPTION]
<!-- Example: TDD mandatory: Tests written → User approved → Tests fail → Then implement; Red-Green-Refactor cycle strictly enforced -->
### TDD / Red-Green-Refactor

### [PRINCIPLE_4_NAME]
<!-- Example: IV. Integration Testing -->
[PRINCIPLE_4_DESCRIPTION]
<!-- Example: Focus areas requiring integration tests: New library contract tests, Contract changes, Inter-service communication, Shared schemas -->
Failing test first, then minimum implementation, then refactor. Unit tests in `#[cfg(test)]` modules. Use `#[test_case]` and snake_case test names. No bullshit tests (tautologies), no flaky tests (no weird sleeps). In tests, const error/status messages and assert against shared constants.

### [PRINCIPLE_5_NAME]
<!-- Example: V. Observability, VI. Versioning & Breaking Changes, VII. Simplicity -->
[PRINCIPLE_5_DESCRIPTION]
<!-- Example: Text I/O ensures debuggability; Structured logging required; Or: MAJOR.MINOR.BUILD format; Or: Start simple, YAGNI principles -->
### Safety & No Silent Failures

## [SECTION_2_NAME]
<!-- Example: Additional Constraints, Security Requirements, Performance Standards, etc. -->
`unsafe_code = "forbid"` workspace-wide. No `.ok()` discards, no silent-default patterns. Library crates use `thiserror` for domain-specific errors; binary edges use `color-eyre`. Missing data, parse failures, dependency errors, closed channels, and invalid state must become typed errors or explicit sanitized logged rejection.

[SECTION_2_CONTENT]
<!-- Example: Technology stack requirements, compliance standards, deployment policies, etc. -->
### No AI Attribution

## [SECTION_3_NAME]
<!-- Example: Development Workflow, Review Process, Quality Gates, etc. -->
No `Generated by Devin`, no `Co-Authored-By: bot`, no attribution in commits, PRs, changelogs, or any authored content. Author all commits and PRs as the human user using the Git identity configured in `~/.gitconfig`.

[SECTION_3_CONTENT]
<!-- Example: Code review requirements, testing gates, deployment approval process, etc. -->
### Worktree + Ship Discipline

## Governance
<!-- Example: Constitution supersedes all other practices; Amendments require documentation, approval, migration plan -->
Non-trivial work in a git worktree on a new branch. Finish by committing with a clear Conventional Commit message, pushing the branch, and opening a draft PR. Do not force-push, push to main/master, or merge without explicit confirmation.

### Dependency Discipline
Comment thread
coderabbitai[bot] marked this conversation as resolved.

Add new dependencies to workspace `Cargo.toml` first, then `workspace = true` in the crate. Prefer versions older than 7 days. No floating ranges (`latest`, `*`, unbounded `>=`). Run `cargo deny check` before adding. Disable default features when they pull unused networking, TLS, compression, native, proc-macro, or runtime surface.

[GOVERNANCE_RULES]
<!-- Example: All PRs/reviews must verify compliance; Complexity must be justified; Use [GUIDANCE_FILE] for runtime development guidance -->
## Quality Gates

- `cargo clippy --all --tests -- -D warnings`
- `cargo nextest run --workspace`
- `cargo deny check`
- `cargo fmt --all`

## Governance

**Version**: [CONSTITUTION_VERSION] | **Ratified**: [RATIFICATION_DATE] | **Last Amended**: [LAST_AMENDED_DATE]
<!-- Example: Version: 2.1.1 | Ratified: 2025-06-13 | Last Amended: 2025-07-16 -->
This constitution is the speckit workflow source of truth. n00n behavior is further constrained by `AGENTS.md`. Amendments require PR review.
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ The workspace lint configuration is the law. It lives in the root `Cargo.toml` `

Every tool result spends context tokens. Prefer structural, pre-indexed tools over broad searches and unfiltered file reads.

- **Explore first:** use `explore` for codebase questions; it routes to `index` (single-file skeleton), `arbor` (callers/callees/blast radius), or `codegraph` (cross-file structure).
- **Explore first:** use `explore` for codebase questions; it routes to `index` (single-file skeleton, priority for file paths), `arbor` (callers/callees/blast radius), `codegraph` (cross-file structure), or `semblem` (keyword/semantic search). The router auto-detects intent from query patterns.
- **Cross-file structure:** use `codegraph` for end-to-end system questions, call paths, and blast-radius impact checks (requires a `.codegraph/` index). Use `arbor` for caller/callee relationships, project map, and diff blast-radius (requires the Arbor CLI).
- **Single-file structure next:** use `index` to get a compact skeleton and exact line ranges before reading any source file.
- **Then targeted reads:** use `read` with `offset`/`limit` for the specific section you need.
Expand All @@ -106,7 +106,7 @@ Every tool result spends context tokens. Prefer structural, pre-indexed tools ov
```

- **Compress structured data:** prefer `n00n.json.tooned` (lossless JSON/TOON passthrough) over plain JSON when passing structured data between tools or scripts.
- **Compress shell output with `rtk`:** the `bash` tool automatically rewrites supported commands through `rtk` when the `rtk` CLI is installed, typically cutting output tokens by 60-90%. Use `bash` (not raw `grep`/`read`/`cat`) for `git`, `cargo`, `rg`, `grep`, `gh`, `find`, `ls`, `cat`, `head`, `tail`, and other system commands. `jq`/`yq` and unsupported flags are run unchanged. Use `rtk proxy <command>` when exact raw shell output is required. Combine with `context-mode` once installed.
- **Compress shell output with `rtk`:** the `bash` tool automatically rewrites supported commands through `rtk` when the `rtk` CLI is installed, typically cutting output tokens by 60-90%. Use `bash` (not raw `grep`/`read`/`cat`) for `git`, `cargo`, `rg`, `grep`, `gh`, `podman`, `docker`, `npm`, `pip`, `python`, `find`, `ls`, `cat`, `head`, `tail`, and other system commands. `jq`/`yq` and unsupported flags are run unchanged. Use `rtk proxy <command>` when exact raw shell output is required. Combine with `context-mode` once installed.
- **Offload reasoning with `thoughtbox`:** use sessions and the knowledge graph for non-trivial reasoning, durable context, and anything that should outlive the session. This keeps the context window focused on the current task.

### Shell and RTK patterns
Expand Down
2 changes: 2 additions & 0 deletions changelog.d/221.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<!-- markdownlint-disable MD041 -->
Added first-tier explore tooling with `explore` intent routing, expanded `arbor`/`codegraph`/`semblem` commands, native/CLI fallbacks, and `rtk`/`bash` hardening.
1 change: 1 addition & 0 deletions deny.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ allow = [
"Unlicense",
"CC0-1.0",
"CC-BY-4.0",
"CDLA-Permissive-2.0",
"MPL-2.0",
"BSL-1.0",
"OpenSSL",
Expand Down
8 changes: 4 additions & 4 deletions n00n-agent/src/prompt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,10 @@ pub const DEFAULT_TONE: &str = r"- Be concise. Your output is displayed on a CLI

const NATIVE_EFFICIENT_TOOLS: &[&str] = &[
"explore",
"arbor (optional, needs Arbor CLI)",
"arbor",
"batch",
"code_execution",
"codegraph (optional, needs .codegraph/ index)",
"codegraph",
"index",
"semblem",
"task",
Expand Down Expand Up @@ -311,7 +311,7 @@ mod tests {
use super::*;
use test_case::test_case;

const NATIVE_EFFICIENT_LINE: &str = "Most efficient tools: explore, arbor (optional, needs Arbor CLI), batch, code_execution, codegraph (optional, needs .codegraph/ index), index, semblem, task, thoughtbox";
const NATIVE_EFFICIENT_LINE: &str = "Most efficient tools: explore, arbor, batch, code_execution, codegraph, index, semblem, task, thoughtbox";

fn slots(prompt: PromptId, entries: &[(Slot, &str)]) -> ResolvedSlots {
let mut slots = ResolvedSlots::default();
Expand Down Expand Up @@ -598,7 +598,7 @@ mod tests {
// because it carries required static instructions that are not meant to shrink.
const SYSTEM_BASELINE: usize = 1645;
const GENERAL_BASELINE: usize = 1759;
const RESEARCH_BASELINE: usize = 1439;
const RESEARCH_BASELINE: usize = 1530;
Comment thread
w0wl0lxd marked this conversation as resolved.
const COMPACTION_USER_BASELINE: usize = 927;
const COMPACTION_BASELINE: usize = 669;
const PLAN_BASELINE: usize = 1031;
Expand Down
2 changes: 1 addition & 1 deletion n00n-agent/src/prompts/general.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ NEVER generate/guess URLs unless for programming help.
- Read before editing; check context/imports to match conventions.
- Prefer edit/multiedit over write; targeted edits use fewer tokens.
- NEVER create files unless necessary. Prefer editing existing files.
- Prefer **codegraph/index/semble** over broad reads, **bash** (rtk except jq/yq) for shell, and **thoughtbox** for reasoning.
- Prefer **explore/index/arbor/codegraph/semblem** for codebase questions, then **read**, then **bash** (rtk except jq/yq) for shell, and **thoughtbox** for reasoning.
{{tool_usage}}

{{efficient_tools}}
Expand Down
15 changes: 10 additions & 5 deletions n00n-agent/src/prompts/research.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
You are a research agent. Explore codebases, gather information, answer questions. Read-only; do not modify files.
<!-- markdownlint-disable MD041 -->
You are a research agent. Explore codebases, gather information, answer questions. Read-only; do not modify files
Comment thread
coderabbitai[bot] marked this conversation as resolved.

Environment:

- Working directory: {cwd}
- Platform: {platform}

# Output discipline

Your response is injected into the parent context; every unnecessary token wastes budget.

- Return a **concise summary** with `file_path:line_number` references.
- Never dump large code blocks; quote only minimal relevant snippets.
- Never write summary/report files to disk.
Expand All @@ -14,17 +18,18 @@ Your response is injected into the parent context; every unnecessary token waste
NEVER generate/guess URLs unless for programming help.

# Tool usage

- Tool results grow context. Minimize verbose calls; prefer compact results.
- Use **batch** for 2+ independent reads/greps/globs. Never call sequentially.
- Use **code_execution** for dependent/chained calls (e.g. glob then read matches) or filtering large outputs.
- codegraph/index/semble for structure; grep; thoughtbox.
- Prefer **explore/index/arbor/codegraph/semblem** for codebase questions, then **read**, then **grep**, and **thoughtbox** for reasoning.
{{tool_usage}}

{{efficient_tools}}

# Guidelines
- Start with codegraph/index/semble, then reads; use thoughtbox.

- Include specific file paths and line numbers when referencing code.
- If unable to find, say so clearly.
- Do not speculate beyond what code shows.
{{instructions}}
- Do not speculate beyond what code shows
{{instructions}}
2 changes: 1 addition & 1 deletion n00n-agent/src/prompts/system.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Be direct and objective. Correct the user when needed.
- Combine **batch** and **task/team/workflow**: launch multiple independent agents or teams in parallel.
- Read before editing. Match context.
- Prefer **edit_lines** / **edit** over full **write**. Use minimal anchor strings to save tokens.
- Prefer **codegraph/index** for structure, **grep** for literals, and **bash** for git/cargo/rg/jq/yq (rewritten via rtk).
- Prefer **explore/index/arbor/codegraph/semblem** for codebase questions, then **read**, then **grep** for literals, and **bash** for git/cargo/rg/jq/yq (rewritten via rtk).
{{tool_usage}}

# Least-privilege tool selection
Expand Down
16 changes: 16 additions & 0 deletions n00n-arbor/src/graph_json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,18 @@ impl GraphIndex {
self.find_neighbors(&self.outgoing, index, Some(CALLS_EDGE_KIND))
}

pub fn caller_count(&self, index: usize) -> usize {
self.incoming.get(&index).map_or(0, |links| {
Self::neighbor_indices(links, Some(CALLS_EDGE_KIND)).len()
})
}

pub fn callee_count(&self, index: usize) -> usize {
self.outgoing.get(&index).map_or(0, |links| {
Self::neighbor_indices(links, Some(CALLS_EDGE_KIND)).len()
})
}

pub fn trace_path_symbols(
&self,
from: &SymbolQuery,
Expand Down Expand Up @@ -352,6 +364,10 @@ impl GraphIndex {
})
.collect()
}

pub fn nodes(&self) -> &[GraphNode] {
&self.nodes
}
}

fn validate_graph_data(data: &GraphData) -> Result<(), ArborError> {
Expand Down
100 changes: 99 additions & 1 deletion n00n-arbor/src/graph_query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@ use std::collections::HashSet;
use std::path::Path;

use crate::graph_json::{GraphIndex, SymbolQuery, SymbolRef};
use crate::{ArborError, Client, Relation, index_health};
use crate::{ArborError, Client, MapEntry, MapSymbol, Relation, index_health};

/// Estimated tokens for a symbol's centrality, caller count, and entry-point flag.
const SYMBOL_METADATA_TOKENS: u64 = 20;
/// Estimated tokens for the per-file entry header.
const ENTRY_OVERHEAD_TOKENS: u64 = 10;

fn line_number(line_start: usize) -> Option<u64> {
// usize -> u64 cannot overflow on 64-bit targets; on other targets we
Expand Down Expand Up @@ -119,6 +124,99 @@ pub fn graph_index_available(project: &Path) -> bool {
index_health::graph_index_available(project)
}

// T067: Native map implementation using graph.json
#[allow(clippy::cast_precision_loss, clippy::similar_names)]
pub fn graph_map(project: &Path, token_budget: Option<u64>) -> Result<Vec<MapEntry>, ArborError> {
let index = load_query_index(project)?;
let mut file_map: std::collections::HashMap<String, Vec<MapSymbol>> =
std::collections::HashMap::new();

for (idx, node) in index.nodes().iter().enumerate() {
let num_callers = index.caller_count(idx);
let num_callees = index.callee_count(idx);
let centrality = if num_callers + num_callees > 0 {
Some((num_callers as f64 + num_callees as f64) / (index.nodes().len() as f64))
} else {
None
};
Comment thread
coderabbitai[bot] marked this conversation as resolved.

let symbol = MapSymbol {
name: node.name.clone(),
kind: node.kind.clone(),
line: node.line_start as u64,
centrality,
callers: Some(num_callers as u64),
is_entry_point: Some(num_callers == 0 && num_callees > 0),
};

file_map.entry(node.file.clone()).or_default().push(symbol);
}

let mut entries: Vec<MapEntry> = file_map
.into_iter()
.map(|(file, symbols)| MapEntry { file, symbols })
.collect();

// Sort by centrality and apply token budget
entries.sort_by(|a, b| {
let max_a = a
.symbols
.iter()
.filter_map(|s| s.centrality)
.fold(0.0_f64, f64::max);
let max_b = b
.symbols
.iter()
.filter_map(|s| s.centrality)
.fold(0.0_f64, f64::max);
max_b.total_cmp(&max_a)
});

if let Some(budget) = token_budget {
let mut total_tokens = 0u64;
let mut filtered = Vec::new();
for entry in entries {
let entry_tokens = estimate_entry_tokens(&entry);
if total_tokens + entry_tokens <= budget {
total_tokens += entry_tokens;
filtered.push(entry);
}
}
entries = filtered;
}

Ok(entries)
}

// T067: Native entry_points implementation using graph.json
pub fn graph_entry_points(project: &Path) -> Result<Vec<Relation>, ArborError> {
let index = load_query_index(project)?;
let mut entries = Vec::new();

for (idx, node) in index.nodes().iter().enumerate() {
if index.caller_count(idx) == 0 && index.callee_count(idx) > 0 {
entries.push(Relation {
name: node.name.clone(),
path: node.file.clone(),
kind: Some(node.kind.clone()),
line: line_number(node.line_start),
});
}
}

Ok(entries)
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

fn estimate_entry_tokens(entry: &MapEntry) -> u64 {
let file_tokens = entry.file.len() as u64;
let symbol_tokens: u64 = entry
.symbols
.iter()
.map(|s| s.name.len() as u64 + s.kind.len() as u64 + SYMBOL_METADATA_TOKENS)
.sum();
file_tokens + symbol_tokens + ENTRY_OVERHEAD_TOKENS
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

#[cfg(test)]
mod tests {
use super::{graph_callers, graph_trace_path};
Expand Down
Loading
Loading