Skip to content

Commit 54ac478

Browse files
authored
feat: add --duration flag to search command (#6)
1 parent 5a0d40a commit 54ac478

3 files changed

Lines changed: 48 additions & 12 deletions

File tree

cmd/tacit/main.go

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ Usage:
9494
tacit status Check daemon status
9595
tacit update Update tacit to the latest version
9696
tacit list [duration] List knowledge entries (default: 24h)
97-
tacit search <pattern> Search knowledge entries by pattern
97+
tacit search [--duration <d>] <pattern> Search knowledge entries by pattern
9898
tacit get <file-path>... Print the full content of one or more knowledge entries
9999
tacit config view Show current configuration
100100
tacit config edit Open configuration in a text editor
@@ -832,25 +832,54 @@ func cmdList() {
832832

833833
// cmdSearch searches the knowledge base for entries matching a pattern.
834834
func cmdSearch() {
835-
if len(os.Args) < 3 {
836-
fmt.Fprintf(os.Stderr, "Usage: tacit search <pattern>\n")
835+
// Parse args: tacit search [--duration <dur>] <pattern>
836+
args := os.Args[2:]
837+
var since time.Time
838+
var patternArgs []string
839+
840+
for i := 0; i < len(args); i++ {
841+
if args[i] == "--duration" && i+1 < len(args) {
842+
d, err := parseDuration(args[i+1])
843+
if err != nil {
844+
fmt.Fprintf(os.Stderr, "Invalid duration %q: %v\n", args[i+1], err)
845+
fmt.Fprintf(os.Stderr, "Examples: 1h, 30m, 24h, 1d, 7d, 2w\n")
846+
os.Exit(1)
847+
}
848+
since = time.Now().Add(-d)
849+
i++ // skip duration value
850+
} else {
851+
patternArgs = append(patternArgs, args[i])
852+
}
853+
}
854+
855+
if len(patternArgs) == 0 {
856+
fmt.Fprintf(os.Stderr, "Usage: tacit search [--duration <duration>] <pattern>\n")
857+
fmt.Fprintf(os.Stderr, "Examples: tacit search meeting, tacit search --duration 1h meeting\n")
837858
os.Exit(1)
838859
}
839860

840-
pattern := os.Args[2]
861+
pattern := patternArgs[0]
841862
baseDir := config.BaseDir()
842863

843-
results, err := search.Search(baseDir, pattern)
864+
results, err := search.Search(baseDir, pattern, since)
844865
if err != nil {
845866
log.Fatalf("Search failed: %v", err)
846867
}
847868

848869
if len(results) == 0 {
849-
fmt.Printf("No results found for %q.\n", pattern)
870+
if !since.IsZero() {
871+
fmt.Printf("No results found for %q in the last %s.\n", pattern, formatDuration(time.Since(since)))
872+
} else {
873+
fmt.Printf("No results found for %q.\n", pattern)
874+
}
850875
return
851876
}
852877

853-
fmt.Printf("Found %d result(s) for %q:\n\n", len(results), pattern)
878+
if !since.IsZero() {
879+
fmt.Printf("Found %d result(s) for %q in the last %s:\n\n", len(results), pattern, formatDuration(time.Since(since)))
880+
} else {
881+
fmt.Printf("Found %d result(s) for %q:\n\n", len(results), pattern)
882+
}
854883
for _, r := range results {
855884
fmt.Printf("[%s] %s / %s\n", r.CreatedAt.Format("2006-01-02 15:04:05"), r.Category, r.Title)
856885
fmt.Printf(" File: %s\n", r.FilePath)

pkg/search/search.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"sort"
1616
"strings"
1717
"sync"
18+
"time"
1819

1920
"github.com/sangmin7648/tacit/pkg/storage"
2021
)
@@ -82,8 +83,9 @@ func isFrontmatterLine(s string) bool {
8283
}
8384

8485
// Search searches the knowledge base at baseDir for pattern (case-insensitive).
86+
// If since is non-zero, only entries created after that time are included.
8587
// Returns results sorted by relevance score descending.
86-
func Search(baseDir, pattern string) ([]*SearchResult, error) {
88+
func Search(baseDir, pattern string, since time.Time) ([]*SearchResult, error) {
8789
rg, err := extractRg()
8890
if err != nil {
8991
return nil, err
@@ -152,6 +154,10 @@ func Search(baseDir, pattern string) ([]*SearchResult, error) {
152154
continue
153155
}
154156

157+
if !since.IsZero() && !entry.CreatedAt.After(since) {
158+
continue
159+
}
160+
155161
score := len(re.FindAllString(entry.Title, -1)) * 10
156162
score += len(re.FindAllString(entry.Category, -1)) * 5
157163
score += len(re.FindAllString(entry.Summary, -1)) * 3

skills/tacit.knowledge/SKILL.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ You are retrieving relevant knowledge from multiple sources: the local tacit kno
1313
## Available tacit Commands
1414

1515
```
16-
tacit list [duration] — List entries created within duration (default: 1h). Supports: 30m, 1h, 24h, 1d, 7d, 2w
17-
tacit search <pattern> — Full-text search across all entries (title + summary + content)
18-
tacit get <file-path> — Print full content of a specific entry
16+
tacit list [duration] — List entries created within duration (default: 1h). Supports: 30m, 1h, 24h, 1d, 7d, 2w
17+
tacit search [--duration <d>] <pattern> — Full-text search across all entries (title + summary + content). Optional --duration limits to entries created within that window.
18+
tacit get <file-path> — Print full content of a specific entry
1919
```
2020

2121
## Process
@@ -37,7 +37,7 @@ Launch one sub-agent per source, all in parallel using the Agent tool. Each sub-
3737

3838
**tacit sub-agent** — retrieves from local knowledge base:
3939
1. Run `tacit list <duration>` to get recent entries
40-
2. Extract 2–4 keywords from the user's prompt and run `tacit search <keyword>` for each
40+
2. Extract 2–4 keywords from the user's prompt and run `tacit search <keyword>` for each. If a time window was identified, pass `--duration <d>` to narrow results (e.g. `tacit search --duration 1h <keyword>`)
4141
3. Merge all unique file paths, prioritizing entries that appear in both list and search results
4242
4. Fetch full content: `tacit get <path1> <path2> ...`
4343
5. Return structured results: `{ source: "tacit", items: [{ title, file_path, date, category, summary, content }] }`
@@ -70,4 +70,5 @@ Synthesize all retrieved knowledge into a direct answer to the user's prompt:
7070
- tacit categories and content are primarily in Korean
7171
- Do NOT fabricate knowledge entries — only reference what actually exists
7272
- `tacit search` supports regex-compatible patterns — you can use `tacit search "키워드1\|키워드2"` to search multiple terms at once
73+
- `tacit search --duration` accepts the same units as `tacit list`: `30m`, `1h`, `24h`, `1d`, `7d`, `2w`
7374
- When launching external source sub-agents, pass the user's original prompt and extracted keywords so each sub-agent can independently determine the best query strategy for its source

0 commit comments

Comments
 (0)