Skip to content

Latest commit

 

History

64 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

denotecli

Denote knowledge base CLI for AI agents — search, read, analyze, and manage 3,000+ org-mode notes

Go stdlib only. Single binary. JSON output. Korean-native.

AI Agent Skill: 에이전트용 스킬 문서는 agent-config 리포의 skills/denotecli/SKILL.md에서 관리합니다.


What This Does

CLI that gives AI agents structured access to a Denote/org-mode knowledge base. Not just search — semantic navigation, Korean↔English bridging, tag governance, and knowledge graph traversal.

./run.sh build              # Build + install
./run.sh test               # 135 tests
./run.sh showcase           # Visual search pattern check
./run.sh cover              # Coverage report

Denote File Format

YYYYMMDDTHHMMSS[==SIGNATURE]--title-with-hyphens[__tag1_tag2].org
  • ID = unique timestamp identifier (the key for everything)
  • Signature = optional alphanumeric code (==5a2, ==0za), used for Denote signatures (e.g. syntopicon/propaedia ordering)
  • Frontmatter: #+title:, #+date:, #+filetags:, #+identifier:
  • Links: [[denote:YYYYMMDDTHHMMSS]]

Examples:

20251107T082610--제목-하이픈-구분__tag1_tag2_tag3.org          # standard
20250904T075937==5a2--힣-ai-에이전트__agents_ai.org           # with signature
20250421T125513==0--†-syntopicon-신토피콘__metameta.org       # single-char signature
20251021T105353--simple-title.org                              # no tags

Commands (12)

Search

denotecli search "에릭 호퍼" --tags bib --max 5       # title/tag/ID search
denotecli search-headings "양자역학" --level 1 --max 10 # heading search across all files
denotecli search-content "LSP 설정" --tags emacs       # full-text grep with tag filter

Read

denotecli read 20250314T125213 --outline --level 2     # heading structure (TOC)
denotecli read 20250314T125213 --offset 41 --limit 20  # specific section by line range

Navigate

denotecli graph 20250314T125213                         # outgoing + incoming links
denotecli keyword-map "이맥스"                          # Korean↔English tag mapping
denotecli keyword-map "emacs"                           # bidirectional

Day / Timeline / Agenda

denotecli day 2023-02-22                               # 특정 날짜 저널/노트/datetree 통합 (생성+수정 노트)
denotecli day --years-ago 3                             # N년 전 오늘
denotecli timeline-journal --month 2023-02             # 월간 저널 활동 개요 (count projection)
denotecli agenda                                       # 오늘 1일 journal 본문 + agent stamps
denotecli agenda 2026-09-11                            # 해당 1일
denotecli agenda 2026-09-11 --week                     # 그 날이 속한 월~일 7일
denotecli agenda --from 2026-09-07 --to 2026-09-13     # 양끝 포함 범위

denotecli day는 다섯 축을 함께 반환합니다.

Field Source Meaning
journal journal/<date>__journal.org 또는 weekly 그날 저널 시간 엔트리
datetree *--diary.org reverse datetree 그날 datetree 엔트리 + CLOCK
notes_created 파일명 ID prefix == 그날 그날 만든 Denote 노트
notes_modified 본문의 #+hugo_lastmod: == 그날 그날 수정된 노트. 생성일은 다른 날
years_ago system time 같은 월일 N년 전인 경우 N

notes_creatednotes_modified상보(complementary) 관계로 정의됩니다 — 같은 날 만들고 그날 hugo_lastmod까지 박은 파일은 notes_created에만 들어가고 notes_modified에서는 제외됩니다 (중복 방지). notes_modified는 명시적인 #+hugo_lastmod: 만 신뢰합니다 (mtime/#+date: fallback 없음). 다양한 org 타임스탬프 포맷 — [2025-06-10], [2025-03-29 Sat 02:06], <2024-01-03 Wed 16:54>, 2023-06-19, Time-stamp: <...> — 모두 첫 YYYY-MM-DD 패턴으로 정규화됩니다.

notes_modified 패스는 모든 노트의 frontmatter head(첫 50줄)만 읽기 때문에 ~3,300 노트 코퍼스에서 100ms 수준을 유지합니다.

journal.entries[]의 기존 time/text는 그대로다. 공유 파서가 TODO 헤딩을 읽게 되면서 todo / body / blocks추가 필드로만 붙는다 (todo"TODO" | "NEXT" | "DONE" | null). 빈 blocks[]이다.

Compatibility — day.datetree는 그대로다

day.datetree는 legacy *diary*.org 한 source{source, entries} 객체이거나, 없으면 null이다. agent stamp를 여기로 밀어 넣지 않는다. 객체→배열 변경도, null[] 변경도 없다. stamp는 아래 agenda 커맨드의 days[].stamps에만 산다. timeline-journal의 월간 count 계약도 바꾸지 않는다.

denotecli agenda — 하루·주간 본문 payload

주간 본문을 daytimeline-journal에 덧씌우지 않는다. 상세 어젠다의 public surface는 새 agenda 커맨드다.

범위

호출 의미
인자 없음 오늘 1일
DATE 해당 1일
DATE --week (DATE 생략 가능) 그 날이 속한 월요일~일요일 7일
--from YYYY-MM-DD --to YYYY-MM-DD 둘을 함께 받으며 양끝 포함(inclusive)

DATE / --week--from/--to를 같이 쓰면 오류다. --from--to는 짝이다. days는 빈 날도 포함해 날짜 오름차순이다. 빈 날은 journal: null, stamps: [].

소스

  • journal: 기존 day와 같은 선택 — journal/ daily 우선, 없으면 weekly fallback. 여러 journal 파일 충돌을 새로 풀지 않는다.
  • stamps: 각 --dirs root 아래 botlog/agenda/*__agenda_*.org를 파일 수 제한 없이 전부. timestamp 다음의 from:을 분리하고, 여러 파일 결과는 시각→source 순으로 결정론 정렬한다. __agenda.org(디바이스 접미사 없음)는 이 glob에 안 걸린다.

제외 (이번 범위 밖)

  • org-agenda의 schedule/deadline/반복 규칙 재현
  • TODO/NEXT/DONE 이외의 임의 Org TODO keyword
  • legacy diary와 agent stamp의 병합 또는 중복 제거
  • Org 링크 렌더링, Markdown 변환, 본문 요약/절단
  • timeline-journal count 계약 변경, agent-config 쪽 바이너리 직접 배포

스키마 (1일 예시)

{
  "from": "2026-09-11",
  "to": "2026-09-11",
  "days": [
    {
      "date": "2026-09-11",
      "day_of_week": "Friday",
      "journal": {
        "source": "/home/.../journal/20260907T000000--2026-09-07__journal_week36.org",
        "format": "weekly",
        "entries": [
          {
            "time": "05:25",
            "text": "§entwurf ...",
            "todo": "TODO",
            "body": "이번브랜치 목표는 ...",
            "blocks": [
              {"kind": "user", "text": "이번브랜치 목표는 ..."},
              {"kind": "assistant", "text": "GLG, GPT-6 ..."}
            ]
          }
        ]
      },
      "stamps": [
        {
          "time": "14:15",
          "text": "agent-config: feat(agenda): ...",
          "from": "pi@oracle",
          "body": "",
          "tags": ["pi", "commit", "agentconfig"],
          "source": "/home/.../botlog/agenda/...__agenda_oracle.org"
        }
      ]
    }
  ]
}
  • body는 해당 시간 헤딩부터 다음 같은/상위 레벨 헤딩 전까지의 본문이다. planning timestamp와 :PROPERTIES: drawer는 제외하고, 하위 헤딩의 내용은 보존한다.
  • [!user] / [!assistant] quote는 blocks[]로도 구조화한다. body에는 wrapper와 marker를 뺀 실제 텍스트를 유지하므로 callout 밖 문장도 잃지 않는다. callout이 없으면 blocks[]이다.
  • 따옴표·역슬래시·개행은 Go JSON encoder가 escape하며 임의 치환하지 않는다. Org/Denote 링크는 원문 유지한다.
  • journal이 없으면 null, stamps가 없으면 []이다.

Manage

denotecli create --title "대화 기록" --tags llmlog,emacs --dir ~/org/llmlog
denotecli rename-tag --from llms --to llm --dry-run     # batch tag rename (preview)
denotecli rename-tag --from llms --to llm               # actual rename (filename + frontmatter)
denotecli tags --top 20                                  # tag statistics
denotecli tags --suggest                                 # stem-based duplicate detection

Performance

Command Scope Time
search 3K files, filenames ~16ms
search-headings 3K files, 60K headings ~30ms
search-content 3K files, 14MB text ~270ms
keyword-map meta notes ~24ms
graph 3K files, backlink scan ~85ms
tags --suggest 2K+ tags, Porter stemmer ~23ms
agenda DATE 1일 journal + stamps ~8ms
agenda DATE --week 7일 envelope ~40ms

Install

git clone https://github.com/junghan0611/denotecli.git
cd denotecli
./run.sh build    # → ~/.local/bin/denotecli

Requires Go 1.21+. No external dependencies (stdlib only).


Output

All output is JSON. Optional fields are omitted when the note does not carry them (signature, lastmod, hugo_lastmod, description, abstract).

{
  "id": "20250904T075937",
  "signature": "5a2",
  "title": "힣-ai-에이전트-편재성-기억-연결",
  "tags": ["agents", "ai"],
  "date": "2025-09-04",
  "lastmod": "2026-05-18",
  "hugo_lastmod": "[2026-05-18 Mon 09:09]",
  "description": "노트 한 줄 요약 (#+description:).",
  "path": "/home/.../meta/20250904T075937==5a2--힣-ai-에이전트-편재성-기억-연결__agents_ai.org"
}

생성 시각과 수정 시각은 다른 축이다

date 는 만들어진 때(#+date:, 또는 Denote ID에서 유도)이고 수정 시각이 아니다. 수정은 #+hugo_lastmod: 하나뿐이며 두 모양으로 함께 나간다:

필드 쓰는 자리
lastmod 2026-05-18 날짜 비교, day 커맨드와 같은 모양
hugo_lastmod [2026-05-18 Mon 09:09] 원본 그대로 — HH:MM이 필요한 비교

시각을 남겨 두는 이유가 실측으로 섰다. 날짜만 보고 판정하면 같은 날 21:55에 찍힌 도장보다 이른 18:32 커밋이 "도장 이후 커밋"으로 잘못 세어진다. 호출자가 org 파일을 따로 정규식으로 파지 않도록, 유도 가능한 값은 이쪽에서 두 모양 다 준다.

search / list / day / read 모두 같은 필드를 싣는다 — "어느 노트가 낡았나"는 한 번의 호출로 답해져야 한다.

abstract — 이 노트가 무엇인가

read (일반·--outline 둘 다)는 첫 헤딩 앞에 놓인 콜아웃 인용 블록을 구조화해서 준다.

#+begin_quote
[!abstract] 이 노트에 대하여

...본문...
#+end_quote
"abstract": { "kind": "abstract", "title": "이 노트에 대하여", "body": "...본문..." }
  • 콜아웃 표식([!xxx])이 없는 평범한 인용은 abstract가 아니다.
  • 첫 헤딩 아래의 콜아웃은 본문이지 노트의 abstract가 아니므로 잡지 않는다.
  • kindabstract 외의 표식(note, tip …)도 그대로 싣는다.

Testing

./run.sh test       # All 135 tests
./run.sh showcase   # Visual: all search patterns with input→output
./run.sh cover      # Coverage report (logic functions: 85-100%)

Showcase tests (go test -v -run TestShowcase) print every search pattern with actual results — makes edge cases visible at a glance.


Project Structure

denotecli/
├── run.sh                    # Build, test, showcase, cover
├── README.md
├── docs/
│   └── obsidian-cli-comparison.md
└── denotecli/                # Go source (single package, 16 modules + 19 test files)
    ├── main.go               # CLI routing + flag parsing
    ├── parser.go             # Denote filename (with signature) + frontmatter + link parser
    ├── search.go             # Directory scanner + title/tag search
    ├── search_headings.go    # Heading search across all files
    ├── search_content.go     # Full-text content search
    ├── read.go               # Read by ID + outline extraction
    ├── graph.go              # Outgoing/incoming link traversal
    ├── keyword_map.go        # Korean↔English keyword mapping
    ├── create.go             # Note creation with Denote naming
    ├── rename_tag.go         # Batch tag rename (filename + frontmatter)
    ├── tags.go               # Tag statistics
    ├── tag_suggest.go        # Stem-based duplicate detection
    ├── day.go                # Day query (journal + datetree + notes)
    ├── agenda.go             # Agenda range (journal body + agent stamps)
    ├── timeline_journal.go   # Monthly journal timeline
    ├── stemmer.go            # Porter stemmer (embedded, no deps)
    └── *_test.go             # 135 tests (unit + integration + showcase)

Related Projects

Project Description
agent-config AI agent configuration — denotecli SKILL.md managed here
zotero-config Headless Zotero + bibcli (sister CLI for 8K+ bibliography)

Author: @junghanacs · License: Apache 2.0

About

Denote-Org Skills for Claude: Extends Anthropic's Life Sciences paradigm from Biology to Life Everything. 3,000+ files validated PKM system.

Topics

Resources

Stars

12 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages