Skip to content
Open
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
1 change: 1 addition & 0 deletions crates/ov_cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ ov read viking://resources/...
- `search` - Context-aware retrieval
- `grep` - Content pattern search
- `glob` - File glob pattern
- `ast-grep` - AST-based code search

### System
- `system wait` - Wait for async processing
Expand Down
22 changes: 22 additions & 0 deletions crates/ov_cli/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,28 @@ impl HttpClient {
self.post("/api/v1/search/glob", &body).await
}

pub async fn ast_grep(
&self,
uri: &str,
pattern: Option<&str>,
rule: Option<&str>,
language: Option<&str>,
file_glob: &str,
limit: i32,
max_file_size_kb: i32,
) -> Result<serde_json::Value> {
let body = serde_json::json!({
"uri": uri,
"pattern": pattern,
"rule": rule,
"language": language,
"file_glob": file_glob,
"limit": limit,
"max_file_size_kb": max_file_size_kb,
});
self.post("/api/v1/search/ast-grep", &body).await
}

// ============ Resource Methods ============

pub async fn add_resource(
Expand Down
19 changes: 19 additions & 0 deletions crates/ov_cli/src/commands/search.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,22 @@ pub async fn glob(
output_success(&result, output_format, compact);
Ok(())
}

pub async fn ast_grep(
client: &HttpClient,
uri: &str,
pattern: Option<&str>,
rule: Option<&str>,
language: Option<&str>,
file_glob: &str,
limit: i32,
max_file_size_kb: i32,
output_format: OutputFormat,
compact: bool,
) -> Result<()> {
let result = client
.ast_grep(uri, pattern, rule, language, file_glob, limit, max_file_size_kb)
.await?;
output_success(&result, output_format, compact);
Ok(())
}
56 changes: 56 additions & 0 deletions crates/ov_cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,28 @@ enum Commands {
#[arg(short, long, default_value = "viking://")]
uri: String,
},
/// Run AST-based code search
AstGrep {
/// Target URI
uri: String,
/// ast-grep pattern (required when --rule is not set)
pattern: Option<String>,
/// Rule file path or inline YAML/JSON content
#[arg(long)]
rule: Option<String>,
/// Language hint
#[arg(short, long)]
language: Option<String>,
/// File glob to scan
#[arg(long, default_value = "**/*")]
file_glob: String,
/// Maximum number of matches to return
#[arg(short = 'n', long, default_value = "200")]
limit: i32,
/// Skip files larger than this size (KB)
#[arg(long, default_value = "512")]
max_file_size_kb: i32,
},
/// Add memory in one shot (creates session, adds messages, commits)
AddMemory {
/// Content to memorize. Plain string (treated as user message),
Expand Down Expand Up @@ -451,6 +473,9 @@ async fn main() {
Commands::Glob { pattern, uri } => {
handle_glob(pattern, uri, ctx).await
}
Commands::AstGrep { uri, pattern, rule, language, file_glob, limit, max_file_size_kb } => {
handle_ast_grep(uri, pattern, rule, language, file_glob, limit, max_file_size_kb, ctx).await
}
};

if let Err(e) = result {
Expand Down Expand Up @@ -706,6 +731,37 @@ async fn handle_glob(pattern: String, uri: String, ctx: CliContext) -> Result<()
commands::search::glob(&client, &pattern, &uri, ctx.output_format, ctx.compact).await
}

async fn handle_ast_grep(
uri: String,
pattern: Option<String>,
rule: Option<String>,
language: Option<String>,
file_glob: String,
limit: i32,
max_file_size_kb: i32,
ctx: CliContext,
) -> Result<()> {
if pattern.is_some() == rule.is_some() {
return Err(crate::error::Error::Client(
"Exactly one of pattern or --rule must be provided".to_string(),
));
}
let client = ctx.get_client();
commands::search::ast_grep(
&client,
&uri,
pattern.as_deref(),
rule.as_deref(),
language.as_deref(),
&file_glob,
limit,
max_file_size_kb,
ctx.output_format,
ctx.compact,
)
.await
}

async fn handle_health(ctx: CliContext) -> Result<()> {
let client = ctx.get_client();
let system_status: serde_json::Value = client.get("/api/v1/observer/system", &[]).await?;
Expand Down
1 change: 1 addition & 0 deletions docs/en/api/01-overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,7 @@ Compact JSON with status wrapper, suitable for scripting. Overrides `--output`:
| POST | `/api/v1/search/search` | Context-aware search |
| POST | `/api/v1/search/grep` | Pattern search |
| POST | `/api/v1/search/glob` | File pattern matching |
| POST | `/api/v1/search/ast-grep` | AST-based code search |

### Relations

Expand Down
59 changes: 59 additions & 0 deletions docs/en/api/03-filesystem.md
Original file line number Diff line number Diff line change
Expand Up @@ -643,6 +643,65 @@ openviking glob "**/*.md" [--uri viking://resources/]

---

### ast_grep()

Search code structure using [ast-grep](https://ast-grep.github.io/).

**Parameters**

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| uri | str | Yes | - | Viking URI to search in |
| pattern | str | No* | - | ast-grep pattern |
| rule | str | No* | - | Rule file path or inline YAML/JSON rule content |
| language | str | No | auto by extension | Language hint for parser |
| file_glob | str | No | `"**/*"` | File glob to scan |
| limit | int | No | 200 | Max returned matches |
| max_file_size_kb | int | No | 512 | Skip files larger than this size |

\* Exactly one of `pattern` or `rule` is required.

**Python SDK (Embedded / HTTP)**

```python
results = client.ast_grep(
uri="viking://resources/",
pattern="def $NAME($$$ARGS):",
language="python",
file_glob="**/*.py",
)
```

**HTTP API**

```
POST /api/v1/search/ast-grep
```

```bash
curl -X POST http://localhost:1933/api/v1/search/ast-grep \
-H "Content-Type: application/json" \
-H "X-API-Key: your-key" \
-d '{
"uri": "viking://resources/",
"pattern": "def $NAME($$$ARGS):",
"language": "python",
"file_glob": "**/*.py"
}'
```

**CLI**

```bash
openviking ast-grep viking://resources/ "def $NAME($$$ARGS):" \
--language python \
--file-glob "**/*.py"
```

For full response examples, see [Retrieval API](06-retrieval.md#ast_grep).

---

### link()

Create relations between resources.
Expand Down
91 changes: 91 additions & 0 deletions docs/en/api/06-retrieval.md
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,97 @@ openviking glob "**/*.md" [--uri viking://resources/]

---

### ast_grep()

Search code structure using [ast-grep](https://ast-grep.github.io/).

**Parameters**

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| uri | str | Yes | - | Viking URI to search in |
| pattern | str | No* | - | ast-grep pattern |
| rule | str | No* | - | Rule file path or inline YAML/JSON rule content |
| language | str | No | auto by extension | Language hint for parser |
| file_glob | str | No | `"**/*"` | File glob to scan |
| limit | int | No | 200 | Max returned matches |
| max_file_size_kb | int | No | 512 | Skip files larger than this size |

\* Exactly one of `pattern` or `rule` is required.

**Python SDK (Embedded / HTTP)**

```python
results = client.ast_grep(
uri="viking://resources/",
pattern="def $NAME($$$ARGS):",
language="python",
file_glob="**/*.py",
limit=100,
)

print(f"Found {results['count']} matches")
for match in results["matches"]:
print(f"{match['uri']}:{match['start_line']}:{match['start_col']}")
print(match["content"])
```

**HTTP API**

```
POST /api/v1/search/ast-grep
```

```bash
curl -X POST http://localhost:1933/api/v1/search/ast-grep \
-H "Content-Type: application/json" \
-H "X-API-Key: your-key" \
-d '{
"uri": "viking://resources/",
"pattern": "def $NAME($$$ARGS):",
"language": "python",
"file_glob": "**/*.py",
"limit": 100
}'
```

**CLI**

```bash
openviking ast-grep viking://resources/ "def $NAME($$$ARGS):" \
--language python \
--file-glob "**/*.py" \
--limit 100
```

**Response**

```json
{
"status": "ok",
"result": {
"matches": [
{
"uri": "viking://resources/app/main.py",
"language": "python",
"start_line": 10,
"start_col": 1,
"end_line": 12,
"end_col": 1,
"content": "def hello(name):\n return f\"Hello {name}\""
}
],
"count": 1,
"scanned_files": 3,
"skipped_files": 0,
"truncated": false
},
"time": 0.1
}
```

---

## Retrieval Pipeline

```
Expand Down
2 changes: 1 addition & 1 deletion docs/en/concepts/01-architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ The Service layer decouples business logic from the transport layer, enabling re

| Service | Responsibility | Key Methods |
|---------|----------------|-------------|
| **FSService** | File system operations | ls, mkdir, rm, mv, tree, stat, read, abstract, overview, grep, glob |
| **FSService** | File system operations | ls, mkdir, rm, mv, tree, stat, read, abstract, overview, grep, glob, ast-grep |
| **SearchService** | Semantic search | search, find |
| **SessionService** | Session management | session, sessions, commit, delete |
| **ResourceService** | Resource import | add_resource, add_skill, wait_processed |
Expand Down
1 change: 1 addition & 0 deletions docs/zh/api/01-overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,7 @@ openviking -o json ls viking://resources/
| POST | `/api/v1/search/search` | 上下文感知搜索 |
| POST | `/api/v1/search/grep` | 模式搜索 |
| POST | `/api/v1/search/glob` | 文件模式匹配 |
| POST | `/api/v1/search/ast-grep` | 基于 AST 的代码搜索 |

### 关联

Expand Down
59 changes: 59 additions & 0 deletions docs/zh/api/03-filesystem.md
Original file line number Diff line number Diff line change
Expand Up @@ -643,6 +643,65 @@ openviking glob "**/*.md" [--uri viking://resources/]

---

### ast_grep()

使用 [ast-grep](https://ast-grep.github.io/) 做代码结构搜索。

**参数**

| 参数 | 类型 | 必填 | 默认值 | 说明 |
|------|------|------|--------|------|
| uri | str | 是 | - | 要搜索的 Viking URI |
| pattern | str | 否* | - | ast-grep 模式 |
| rule | str | 否* | - | 规则文件路径或内联 YAML/JSON 规则内容 |
| language | str | 否 | 按扩展名自动推断 | 语言提示 |
| file_glob | str | 否 | `"**/*"` | 要扫描的文件 glob |
| limit | int | 否 | 200 | 最多返回匹配数 |
| max_file_size_kb | int | 否 | 512 | 跳过超过该大小的文件 |

\* `pattern` 和 `rule` 必须且只能提供一个。

**Python SDK (Embedded / HTTP)**

```python
results = client.ast_grep(
uri="viking://resources/",
pattern="def $NAME($$$ARGS):",
language="python",
file_glob="**/*.py",
)
```

**HTTP API**

```
POST /api/v1/search/ast-grep
```

```bash
curl -X POST http://localhost:1933/api/v1/search/ast-grep \
-H "Content-Type: application/json" \
-H "X-API-Key: your-key" \
-d '{
"uri": "viking://resources/",
"pattern": "def $NAME($$$ARGS):",
"language": "python",
"file_glob": "**/*.py"
}'
```

**CLI**

```bash
openviking ast-grep viking://resources/ "def $NAME($$$ARGS):" \
--language python \
--file-glob "**/*.py"
```

完整响应示例见:[检索 API](06-retrieval.md#ast_grep)。

---

### link()

创建资源之间的关联。
Expand Down
Loading
Loading