Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,26 @@

All notable changes to srcwalk are documented here.

## [1.8.0] - 2026-08-16

### Added
- Structural owner attribution for C, C++, and Ruby in `discover --match any --as text`, extending owner coverage from 8 to 11 languages. C++ owners carry namespace/type qualification (`ns::Class::method`) including out-of-line definitions and operators; templates are transparent and lambdas are barriers. Ruby owners distinguish instance (`A::B#m`) from singleton (`A::B.m`) definitions, support one `class << self` level, and keep reopened classes as independent sites.
- Fail-closed abstention across the new languages: macro-generated (e.g. `TEST(...)`) and K&R C definitions, Ruby dynamic metaprogramming (`define_method`, eval/exec families, `alias`, `attr_*`), unprovable qualified compositions, and error-degraded regions never fabricate owners.
- Typed owner-abstention transparency: a file that was analyzed but declined attribution now reports parser-known reasons once per file (`parse-failed`, `error-line`, `barrier`, `top-level`, `tie`); silence now reliably means the language has no owner analyzer. Existing named-owner output is byte-for-byte unchanged.
- `srcwalk update`/`update --check`: installation-aware self-update that detects the npm/pnpm/yarn/bun channel and re-runs its global install command, or, for a standalone/cargo install, downloads the matching release archive, verifies its SHA-256, and swaps it in with a verified, transactional replace. Network access is explicit-only through `update`, `update --check`, or the compatibility command `version --check`; plain version and navigation commands remain offline.

### Fixed
- Valid C++ `operator()` definitions (inline, const, out-of-line, namespace-qualified) attribute owners instead of degrading to anonymous barriers.
- GUIDE and README no longer describe the multi-language owner rollup as Go-only; the mechanical call appendix remains Go-only.

### Evidence boundary
- Owner and abstention labels are structural lexical evidence, not runtime binding, dispatch, or importance ranking; `top-level` is a lexical position and top-level configuration may still be the most relevant evidence.
- No call analysis runs for any non-Go language.

### Known limitations
- The C++ comma operator spelling (`operator,`) still abstains.
- Dotted bare terms inside batch discover queries still resolve as free-text for the dotted term (unchanged from 1.7.0).

## [1.7.0] - 2026-08-13

### Added
Expand Down
74 changes: 73 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "srcwalk"
version = "1.7.0"
version = "1.8.0"
edition = "2021"
description = "Tree-sitter indexed lookups — smart code reading for AI agents"
license = "MIT"
Expand Down Expand Up @@ -74,6 +74,9 @@ tree-sitter-scss = "1.0.0"
tree-sitter-less = "1.0.0"
tree-sitter-html = "0.23.2"

# Standalone update integrity verification (US-074)
sha2 = "0.10"


[dev-dependencies]
tempfile = "3"
Expand Down
13 changes: 11 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,15 @@ curl -L https://github.com/sting8k/srcwalk/releases/latest/download/srcwalk-aarc

After installing the CLI, `srcwalk guide` prints the full embedded, version-matched agent guide. The installable skill entry is [`skills/srcwalk/SKILL.md`](./skills/srcwalk/SKILL.md); it bootstraps agents to that embedded guide in the installed binary.

## Upgrade

```sh
srcwalk update # apply the latest release
srcwalk update --check # resolve and compare only, no download or install
```

npm/pnpm/yarn/bun installs re-run that package manager's global install command. A `cargo install`/pre-built-binary install instead downloads the matching release archive, verifies its SHA-256, and swaps it in with a verified, transactional replace. Network access only happens when you explicitly run `update`, `update --check`, or the compatibility command `version --check` — never on a plain `version` or any navigation command.

## Release notes

See [`CHANGELOG.md`](./CHANGELOG.md) for curated release notes. Maintainers should update the matching changelog section before pushing a `vX.Y.Z` tag; the release workflow uses that section as the GitHub Release body.
Expand Down Expand Up @@ -409,9 +418,9 @@ source: text · kind: name occurrence · confidence: text evidence
</details>

<details>
<summary><b>Discover — Go owner rollup and mechanical call evidence</b></summary>
<summary><b>Discover — owner rollup and Go-only mechanical call evidence</b></summary>

For compact Go `discover --as text --match any` output, matching file rows may include a narrowest-owner rollup and a bounded mechanical call appendix. Owner ranges are candidate exact reads (dereference them with `srcwalk show`), not runtime binding proof.
For compact `discover --as text --match any` output, matching file rows may include a narrowest-owner rollup (multi-language) and a bounded mechanical call appendix (Go-only). Owner ranges are candidate exact reads (dereference them with `srcwalk show`), not runtime binding proof.

```
$ srcwalk discover "apply body,set body,connect body" --match any --as text --scope .
Expand Down
19 changes: 19 additions & 0 deletions npm/install.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,25 @@ async function testTarRoundTrip() {
}
}

// Shared pinned fixture (kept outside npm/, so it is never part of the
// packed payload): cross-checks PLATFORM_MAP against the exact same table
// `release_target_for` asserts against in src/main_version_tests.rs, so the
// two cannot silently drift.
const fixturePath = path.join(__dirname, "..", "tests", "fixtures", "release-targets.json");
const fixtureRows = JSON.parse(fs.readFileSync(fixturePath, "utf8"));
assert.strictEqual(
fixtureRows.length,
Object.keys(PLATFORM_MAP).length,
"fixture row count must match PLATFORM_MAP",
);
for (const row of fixtureRows) {
const key = `${row.nodePlatform}-${row.nodeArch}`;
const platform = PLATFORM_MAP[key];
assert.ok(platform, `PLATFORM_MAP missing ${key}`);
assert.strictEqual(platform.target, row.target, `${key} target mismatch`);
assert.strictEqual(platform.binName, row.binary, `${key} binary mismatch`);
}

testTarRoundTrip()
.then(() => console.log("PASS: npm installer checksum, redirect, archive, tar, and platform guards"))
.catch((error) => {
Expand Down
2 changes: 1 addition & 1 deletion npm/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "srcwalk",
"version": "1.7.0",
"version": "1.8.0",
"description": "Code-intelligence CLI for AI agents — tree-sitter outlines, symbol search, caller/callee graphs, deps, overview",
"bin": {
"srcwalk": "run.js"
Expand Down
6 changes: 3 additions & 3 deletions skills/srcwalk/GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Default to srcwalk first for code-structure work. Use this contract to find exact evidence, choose the next read, and keep claims bounded before shell search.

Keep `--scope` narrow. Use `rg`, `read`, `fd`, `find`, or shell filesystem tools only for final text/regex confirmation, filesystem metadata, generated-output cleanup, or when srcwalk lacks structural support. If you bypass srcwalk for a code claim, say why.
Keep `--scope` narrow. Use `rg`, `read`, `fd`, `find`, or shell filesystem tools only for final text/regex confirmation, filesystem metadata, generated-output cleanup, or when srcwalk lacks structural support. If you bypass srcwalk for a code claim, say why. srcwalk's evidence/navigation commands are all offline; network access is explicit through `srcwalk update`, `update --check`, or compatibility `version --check`.

## Choose one route first

Expand Down Expand Up @@ -70,8 +70,8 @@ If `discover` prints `## Confirmed structural targets`, run the printed `> Next:

Symbol discovery separates parser-backed definitions from text-matched name occurrences. Repeated same-name definitions receive an ambiguity caveat. Text discovery remains literal evidence; `--match all` is same-file co-occurrence, not semantic relation proof.

For Go `discover --as text --match any`, matched lines may carry a structural owner rollup and a bounded mechanical call appendix:
- `owners (#N=...; *K=hits)` lists each matched line's narrowest structural owner by query-term position; the range is a candidate exact read (`srcwalk show`), not relation or runtime binding proof.
For `discover --as text --match any`, matched lines may carry a multi-language structural owner rollup and a Go-only bounded mechanical call appendix:
- `owners (#N=...; *K=hits)` lists each matched line's narrowest structural owner by query-term position; the range is a candidate exact read (`srcwalk show`), not relation or runtime binding proof. An analyzed file that declined reports `owners: none — abstained (...)` or `owner abstentions: ...` (`parse-failed`, `error-line`, `barrier`, `top-level`, `tie`); silence means the language has no owner analyzer, and absence is not an abstention claim. `top-level` is a lexical position, not an importance judgment.
- Edge labels: `[recv]`=same package-qualified receiver type, `[local]`=single-assignment constructor local, `[bare]`=same-package invocation.
- `calls NAME` names the call expression, not the candidate binding; the candidate is a mechanically filtered definition candidate, not a proven binding.
- Edges are mechanically filtered direct-call navigation (absence does not rule out dynamic dispatch, DI, callbacks, or protocol wiring); `@:` = candidate in the same call file, cross-file keeps the repo-relative path. Capped, deterministically ordered; zero-edge owners abstain with an explicit caveat.
Expand Down
15 changes: 12 additions & 3 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ pub(crate) struct Cli {
}

pub(crate) const ROOT_HELP: &str = "\
Start here:\n srcwalk guide Full embedded, version-matched agent guide for agents\n\nCommon:\n srcwalk overview Show repo orientation and dependency groups\n srcwalk context <symbol-or-file:line> Understand one known target\n srcwalk trace callers <symbol> Show who calls a symbol\n srcwalk trace callees <symbol> Show what a symbol calls\n srcwalk deps <file> Show imports and dependents\n srcwalk assess <symbol> Heuristic blast-radius triage\n srcwalk review <range-or-staged> Review a change set with Flow Map evidence\n srcwalk compare <target-a> <target-b> Compare two known source targets structurally\n srcwalk discover <query> Find candidate symbols/name occurrences/text\n srcwalk discover <glob> --as file Find files by glob\n srcwalk show <path>:<line> -C 10 Read exact evidence with extra line context\n srcwalk <path> Read a file smartly\n srcwalk <path>:<line> Read around a line\n srcwalk version Show version; add --check for latest";
Start here:\n srcwalk guide Full embedded, version-matched agent guide for agents\n\nCommon:\n srcwalk overview Show repo orientation and dependency groups\n srcwalk context <symbol-or-file:line> Understand one known target\n srcwalk trace callers <symbol> Show who calls a symbol\n srcwalk trace callees <symbol> Show what a symbol calls\n srcwalk deps <file> Show imports and dependents\n srcwalk assess <symbol> Heuristic blast-radius triage\n srcwalk review <range-or-staged> Review a change set with Flow Map evidence\n srcwalk compare <target-a> <target-b> Compare two known source targets structurally\n srcwalk discover <query> Find candidate symbols/name occurrences/text\n srcwalk discover <glob> --as file Find files by glob\n srcwalk show <path>:<line> -C 10 Read exact evidence with extra line context\n srcwalk <path> Read a file smartly\n srcwalk <path>:<line> Read around a line\n srcwalk version Show version; add --check for latest\n srcwalk update Update to the latest release; add --check to preview";

pub(crate) const GUIDE: &str = include_str!("../skills/srcwalk/GUIDE.md");

Expand Down Expand Up @@ -80,6 +80,8 @@ pub(crate) enum Command {
Show(ShowCmd),
/// Show version, optionally checking the latest release.
Version(VersionCmd),
/// Update srcwalk to the latest release, or check with --check.
Update(UpdateCmd),
/// Structural decision-flow compatibility primitive for review internals.
#[command(hide = true)]
DecisionFlow(DecisionFlowCmd),
Expand All @@ -90,7 +92,14 @@ pub(crate) enum Command {

#[derive(Args)]
pub(crate) struct VersionCmd {
/// Check GitHub for the latest release and print update commands if newer.
/// Check GitHub for the latest release and print "Run: srcwalk update" if newer.
#[arg(long)]
pub(crate) check: bool,
}

#[derive(Args)]
pub(crate) struct UpdateCmd {
/// Resolve and compare only; never downloads, execs a package manager, or writes files.
#[arg(long)]
pub(crate) check: bool,
}
Expand Down Expand Up @@ -492,7 +501,7 @@ impl RunConfig {

pub(crate) fn from_command(command: Command) -> Option<Self> {
match command {
Command::Guide | Command::Version(_) => None,
Command::Guide | Command::Version(_) | Command::Update(_) => None,
Command::Discover(cmd) => Some(Self::from_discover(cmd)),
Command::Show(cmd) => Some(Self::from_show(cmd)),
Command::Trace(cmd) => Some(Self::from_trace(cmd)),
Expand Down
8 changes: 3 additions & 5 deletions src/commands/decision_flow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,11 +150,9 @@ fn resolve_existing_file(raw: &str, scope: &Path) -> Option<PathBuf> {
}
}

candidates.into_iter().find(|candidate| {
std::fs::metadata(candidate)
.ok()
.is_some_and(|meta| meta.is_file())
})
candidates
.into_iter()
.find(|candidate| std::fs::metadata(candidate).is_ok_and(|meta| meta.is_file()))
}

fn looks_like_path(value: &str) -> bool {
Expand Down
Loading
Loading