Add TaskFileService, which writes `status: pending` task files to
OpenAugi/Tasks/ via the Obsidian vault API — no shell-out, no HTTP, no
Node modules, so it works on mobile too. The file format mirrors the
parent repo's templates/task-template.md contract consumed by
task_watcher.py.
New commands (all platforms):
- Augi: Run review pass -> "run the review pass"
- Augi: Process dashboard -> "process the dashboard"
- Augi: Distill selection -> selection/active-note body becomes the
## Context; distill-lens instruction
Deprecate the legacy Task Dispatch feature (it launches tmux itself,
bypassing the watcher). Kept functional for community users without the
Python watcher; steer new use to task files via docs banner, settings UI
label, and a @deprecated marker. To be removed over a release or two.
Docs: new docs/AGENT_TASKS.md; README + CODEBASE_MAP updated to lead with
the task-file flow. Tests: 18 new, including a watcher-frontmatter-regex
compat check and same-second collision handling.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
17 KiB
OpenAugi Codebase Map
A comprehensive reference for navigating and extending this Obsidian plugin.
Quick Reference
| What | Where |
|---|---|
| Plugin entry point | main.ts |
| Settings types & defaults | types/settings.ts |
| OpenAI API integration | services/openai-service.ts |
| File I/O operations | services/file-service.ts |
| Link traversal & aggregation | services/distill-service.ts |
| Unified context discovery | services/context-gathering-service.ts |
| Settings UI | ui/settings-tab.ts |
| Agent task-file writer | services/task-file-service.ts |
| Task dispatch service (deprecated) | services/task-dispatch-service.ts |
| Task dispatch types | types/task-dispatch.ts |
| Context modals | ui/context-gathering-modal.ts, ui/context-selection-modal.ts, ui/context-preview-modal.ts |
| Session list modal | ui/session-list-modal.ts |
Project Structure
src/
├── main.ts # Plugin entry, command registration, service init
├── types/
│ ├── plugin.ts # Plugin interface
│ ├── settings.ts # Settings interfaces & defaults
│ ├── context.ts # Context gathering types
│ └── transcript.ts # API response types
├── services/
│ ├── openai-service.ts # OpenAI API calls
│ ├── file-service.ts # File creation & output
│ ├── distill-service.ts # Content aggregation, link traversal
│ ├── context-gathering-service.ts # Unified discovery orchestration
│ ├── task-file-service.ts # Pending task files → OpenAugi/Tasks/ (watcher trigger)
│ └── task-dispatch-service.ts # tmux session & agent dispatch (deprecated)
├── types/
│ ├── plugin.ts # Plugin interface
│ ├── settings.ts # Settings interfaces & defaults
│ ├── context.ts # Context gathering types
│ ├── transcript.ts # API response types
│ └── task-dispatch.ts # Task dispatch types (agents, sessions, frontmatter)
├── ui/
│ ├── settings-tab.ts # Settings panel
│ ├── loading-indicator.ts # Status bar spinner
│ ├── context-gathering-modal.ts # Stage 1: Discovery config
│ ├── context-selection-modal.ts # Stage 2: Checkbox selection
│ ├── context-preview-modal.ts # Stage 3: Preview & action
│ ├── prompt-selection-modal.ts # Custom prompt picker
│ ├── session-list-modal.ts # Task dispatch session list
│ └── recent-activity-modal.ts # (Legacy)
└── utils/
└── filename-utils.ts # Sanitization, backlink mapping
Services Architecture
OpenAIService (openai-service.ts)
Handles all AI interactions.
Key Methods:
parseTranscript(content)- Voice transcript → atomic notes + tasks + summarydistillContent(content, customPrompt?)- Multiple notes → atomic notes + summarypublishContent(content, customPrompt?)- Notes → single polished blog post
API Details:
- Endpoint:
https://api.openai.com/v1/chat/completions - Model: Configurable (GPT-5, GPT-5 Mini, GPT-5 Nano, or custom)
- Uses JSON Schema for structured outputs (distill/transcript)
- Free-form text for publishing
Prompt Customization:
- Extracts
context:sections from notes to customize processing - Supports custom prompt files from
OpenAugi/Prompts/
FileService (file-service.ts)
Manages all file output.
Key Methods:
writeTranscriptFiles(filename, data)- Creates summary + atomic notes from transcriptwriteDistilledFiles(rootFile, data)- Creates distilled summary + atomic noteswritePublishedPost(content, sourceNotes, promptName)- Creates published post with frontmatter
Output Organization:
OpenAugi/
├── Summaries/ # Summary files
│ ├── [name] - summary.md
│ └── [name] - distilled.md
├── Notes/ # Atomic notes in session folders
│ ├── Transcript YYYY-MM-DD HH-mm-ss/
│ └── Distill [Name] YYYY-MM-DD HH-mm-ss/
├── Published/ # Blog posts
│ └── [Title] - Published YYYY-MM-DD.md
└── Prompts/ # Custom prompt templates
Features:
- Automatic collision handling (appends -1, -2, etc.)
- Backlink mapping (original titles → sanitized filenames)
- Session-based folders for organization
DistillService (distill-service.ts)
Content discovery and aggregation.
Discovery Methods:
getLinkedNotes(file)- Get all forward-linked notes from a filegetBacklinksForFile(targetFile)- Get all notes that link TO the target filegetBacklinkSnippets(targetFile, sourceFile, contextLines)- Extract context around backlinksgetRecentlyModifiedNotes(daysBack, excludeFolders, fromDate?, toDate?)- Time-based discovery
Aggregation:
aggregateContent(files, timeWindowDays?)- Combine notes into single content string
Link Discovery Supports:
[[wikilinks]]and embeds- Dataview queries (if plugin available)
- Checkbox collections: only
[x]checked items - Backlinks - Notes that link TO discovered notes (header section/line extraction)
Journal Filtering:
- Detects date headers (e.g.,
### YYYY-MM-DD) - Extracts only sections within time window
- Configurable date format
ContextGatheringService (context-gathering-service.ts)
Orchestrates the unified discovery system.
Main Method:
gatherContext(config: ContextGatheringConfig): Promise<GatheredContext>
Discovery Modes:
- Linked Notes (BFS) - Breadth-first traversal up to 3 levels
- Recent Activity - Time-based discovery
Features:
- Bidirectional link traversal (forward links + backlinks at each depth level)
- Forward links: extract full note content
- Backlinks: extract header section/lines around the link reference
- Character limit enforcement
- Folder exclusion filtering
- Returns discovered notes with metadata (depth, source, size, isBacklink)
TaskFileService (task-file-service.ts)
Writes status: pending task files to OpenAugi/Tasks/ via the vault API — the trigger contract consumed by the OpenAugi task watcher (openaugi up in the parent repo). No Node.js modules, so it works on mobile. See AGENT_TASKS.md.
Key Methods:
createReviewPassTask()- Queue "run the review pass"createProcessDashboardTask()- Queue "process the dashboard"createDistillTask(context, sourceNote)- Queue a distill of the given content (selection or note body)
Contract:
- File format defined in the parent repo's
src/openaugi/templates/task-template.md - Frontmatter:
status: pending,source_block_id: obsidian-plugin,source_note - Sections:
# title,## Context,## User instruction,## Task,## Human Todo,## Results - No
repo/working_dirkey — the watcher defaults the agent to the vault
TaskDispatchService (task-dispatch-service.ts) — DEPRECATED
Manages tmux-based agent sessions dispatched from task notes. Deprecated: it launches tmux itself, bypassing the task watcher. Kept functional for existing users; to be removed over a release or two. Prefer TaskFileService.
Key Methods:
launchOrAttach(file)- If tmux session exists, attach; otherwise assemble context, create session, start agentkillSession(file)- Kill tmux session for the given task note, update frontmatterlistActiveSessions()- List alltask-*tmux sessionskillSessionById(taskId)- Kill session by ID (from session list modal)openTerminal(sessionName)- Open terminal app attached to tmux session
Context Assembly:
- Reads task note body (strips frontmatter)
- Gets linked notes via
DistillService.getLinkedNotes() - Aggregates via
DistillService.aggregateContent() - Writes to temp file at
/tmp/openaugi/task-{id}-context.md - Context injected into agent via
--append-system-prompt-file(Claude Code)
Frontmatter:
- Reads
task_id,agent,statusviametadataCache.getFileCache() - Updates
session_active,last_sessionviafileManager.processFrontMatter()
Shell Execution:
- Uses Node.js
child_process.exec(available via Electron) - tmux commands:
has-session,new-session,send-keys,kill-session,list-sessions - Terminal opening:
osascriptfor iTerm2 or Terminal.app
Types Reference
Settings (types/settings.ts)
interface OpenAugiSettings {
apiKey: string
defaultModel: 'gpt-5' | 'gpt-5-mini' | 'gpt-5-nano'
customModelOverride: string
summaryFolder: string // Default: 'OpenAugi/Summaries'
notesFolder: string // Default: 'OpenAugi/Notes'
promptsFolder: string // Default: 'OpenAugi/Prompts'
publishedFolder: string // Default: 'OpenAugi/Published'
useDataviewIfAvailable: boolean
enableDistillLogging: boolean
recentActivityDefaults: {
daysBack: number // Default: 7
excludeFolders: string[] // Default: ['Templates', 'Archive', 'OpenAugi']
filterJournalSections: boolean
dateHeaderFormat: string // Default: '### YYYY-MM-DD'
}
contextGatheringDefaults: {
linkDepth: 1 | 2 | 3 // Default: 1
maxCharacters: number // Default: 100000
filterRecentSectionsOnly: boolean
includeBacklinks: boolean // Default: true
backlinkContextLines: number // Default: 0 (0 = header section)
}
}
Context Types (types/context.ts)
interface ContextGatheringConfig {
sourceMode: 'linked-notes' | 'recent-activity'
rootNote?: TFile
linkDepth: 1 | 2 | 3
maxCharacters: number
timeWindow?: {
mode: 'days-back' | 'date-range'
daysBack?: number
fromDate?: string
toDate?: string
}
excludeFolders: string[]
filterRecentSectionsOnly: boolean
journalSectionDays?: number
includeBacklinks?: boolean // Enable backlink discovery
backlinkContextLines?: number // 0 = header section, N = lines before/after
}
interface DiscoveredNote {
file: TFile
depth: number // 0 = root, 1-3 = linked depth
discoveredVia: string // "root" | "linked from [[X]]" | "backlink from [[X]]" | "recent activity"
estimatedChars: number
included: boolean // false if exceeded character limit
isBacklink: boolean // true if discovered via backlink
backlinkSnippet?: string // Extracted header section/lines (backlinks only)
backlinkLine?: number // Line number of link (backlinks only)
}
interface GatheredContext {
notes: DiscoveredNote[]
aggregatedContent: string
totalCharacters: number
totalNotes: number
config: ContextGatheringConfig
timestamp: string
}
Response Types (types/transcript.ts)
interface TranscriptResponse {
summary: string
notes: Array<{ title: string; content: string }>
tasks: string[]
}
interface DistillResponse extends TranscriptResponse {
sourceNotes: string[]
}
Command Registration
Commands are registered in main.ts:
| Command ID | Name | Purpose |
|---|---|---|
parse-transcript |
Parse transcript | Process voice transcript (legacy) |
distill-notes |
Distill linked notes | Distill with prompt selection (legacy) |
openaugi-process-notes |
Process notes | Unified flow: linked notes |
openaugi-process-recent |
Process recent activity | Unified flow: recent activity |
openaugi-save-context |
Save context | Save raw aggregated content |
augi-run-review-pass |
Augi: Run review pass | Write pending task file: "run the review pass" |
augi-process-dashboard |
Augi: Process dashboard | Write pending task file: "process the dashboard" |
augi-distill-selection |
Augi: Distill selection | Write pending distill task for selection/active note |
task-dispatch-launch |
Task dispatch: Launch or attach | Deprecated — launch or attach to agent tmux session |
task-dispatch-kill |
Task dispatch: Kill session | Deprecated — kill active tmux session for task note |
task-dispatch-list |
Task dispatch: List active sessions | Deprecated — show modal of all active task sessions |
Unified Three-Stage Pipeline
The new commands use a consistent flow:
┌─────────────────────────────────────┐
│ Stage 1: ContextGatheringModal │
│ - Choose source mode │
│ - Set depth, limits, filters │
│ - Click "Discover Notes" │
└──────────────┬──────────────────────┘
↓
┌─────────────────────────────────────┐
│ Stage 2: ContextSelectionModal │
│ - Checkbox list of discovered notes│
│ - Toggle individual notes │
│ - See character/token counts │
└──────────────┬──────────────────────┘
↓
┌─────────────────────────────────────┐
│ Stage 3: ContextPreviewModal │
│ - Final preview of context │
│ - Path A: Save raw (no AI) │
│ - Path B: Process with AI │
│ → PromptSelectionModal │
│ → Choose: Distill or Publish │
└─────────────────────────────────────┘
Key Algorithms
BFS Link Traversal
Used by ContextGatheringService.discoverLinkedNotes():
Queue = [RootNote at depth 0]
while Queue not empty:
note = Queue.dequeue()
if totalChars + note.size > maxChars:
mark note as excluded (overflow)
continue
mark note as included
totalChars += note.size
if depth < maxDepth:
for each link in note:
if not already discovered:
Queue.enqueue(linkedNote at depth + 1)
Content Aggregation Format
Notes are combined with clear boundaries:
# Note: [Note Title 1]
[Full content of note 1]
# Note: [Note Title 2]
[Full content of note 2]
Backlink Mapping
During file creation:
- Register mapping:
"Original Title"→"sanitized-filename" - When writing content, replace all
[[Original Title]]with[[sanitized-filename]] - Ensures backlinks work despite filename sanitization
Extending the Plugin
Adding a New Command
- Define command handler in
main.ts - Register with
addCommand({ id, name, callback }) - Use existing services or create new ones
Adding a New Service
- Create file in
src/services/ - Export class with constructor accepting
Appand dependencies - Initialize in
main.tsinitializeServices() - Add to plugin class properties
Adding Settings
- Add property to
OpenAugiSettingsintypes/settings.ts - Add default value to
DEFAULT_SETTINGS - Add UI control in
ui/settings-tab.tsdisplay()method
Adding a New Modal
- Create file in
src/ui/ - Extend
Modalfrom Obsidian - Implement
onOpen()andonClose() - Use callback pattern for returning results
Build & Development
npm run dev # Development build with watch
npm run build # Production build
npm run typecheck # TypeScript checking
Publishing
See PUBLISHING.md for the complete release process.
Output Folders
| Folder | Purpose | Example Files |
|---|---|---|
OpenAugi/Summaries/ |
Summary/distilled files | MyNote - distilled.md |
OpenAugi/Notes/ |
Atomic note sessions | Distill MyNote 2025-01-12 14-30-00/ |
OpenAugi/Published/ |
Blog posts | My Post - Published 2025-01-12.md |
OpenAugi/Prompts/ |
Custom prompt templates | Technical Focus.md |
OpenAugi/Logs/ |
Debug logs (if enabled) | Distill context logs |
Obsidian APIs Used
Plugin- Base plugin classApp- Vault and workspace accessTFile- File abstractionModal- Dialog windowsNotice- Toast notificationsPluginSettingTab- Settings panelMetadataCache- Link resolutionVault- File read/write operations
External Dependencies
- OpenAI API - Chat completions endpoint
- Dataview Plugin (optional) - Query execution for advanced link discovery