mirror of
https://github.com/bitsofchris/openaugi-obsidian-plugin.git
synced 2026-07-22 12:40:27 +00:00
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>
470 lines
17 KiB
Markdown
470 lines
17 KiB
Markdown
# OpenAugi Codebase Map
|
|
|
|
A comprehensive reference for navigating and extending this Obsidian plugin.
|
|
|
|
## Quick Reference
|
|
|
|
| What | Where |
|
|
|------|-------|
|
|
| Plugin entry point | [main.ts](../src/main.ts) |
|
|
| Settings types & defaults | [types/settings.ts](../src/types/settings.ts) |
|
|
| OpenAI API integration | [services/openai-service.ts](../src/services/openai-service.ts) |
|
|
| File I/O operations | [services/file-service.ts](../src/services/file-service.ts) |
|
|
| Link traversal & aggregation | [services/distill-service.ts](../src/services/distill-service.ts) |
|
|
| Unified context discovery | [services/context-gathering-service.ts](../src/services/context-gathering-service.ts) |
|
|
| Settings UI | [ui/settings-tab.ts](../src/ui/settings-tab.ts) |
|
|
| Agent task-file writer | [services/task-file-service.ts](../src/services/task-file-service.ts) |
|
|
| Task dispatch service (deprecated) | [services/task-dispatch-service.ts](../src/services/task-dispatch-service.ts) |
|
|
| Task dispatch types | [types/task-dispatch.ts](../src/types/task-dispatch.ts) |
|
|
| Context modals | [ui/context-gathering-modal.ts](../src/ui/context-gathering-modal.ts), [ui/context-selection-modal.ts](../src/ui/context-selection-modal.ts), [ui/context-preview-modal.ts](../src/ui/context-preview-modal.ts) |
|
|
| Session list modal | [ui/session-list-modal.ts](../src/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 + summary
|
|
- `distillContent(content, customPrompt?)` - Multiple notes → atomic notes + summary
|
|
- `publishContent(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 transcript
|
|
- `writeDistilledFiles(rootFile, data)` - Creates distilled summary + atomic notes
|
|
- `writePublishedPost(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 file
|
|
- `getBacklinksForFile(targetFile)` - Get all notes that link TO the target file
|
|
- `getBacklinkSnippets(targetFile, sourceFile, contextLines)` - Extract context around backlinks
|
|
- `getRecentlyModifiedNotes(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:**
|
|
```typescript
|
|
gatherContext(config: ContextGatheringConfig): Promise<GatheredContext>
|
|
```
|
|
|
|
**Discovery Modes:**
|
|
1. **Linked Notes (BFS)** - Breadth-first traversal up to 3 levels
|
|
2. **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](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_dir` key — 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 agent
|
|
- `killSession(file)` - Kill tmux session for the given task note, update frontmatter
|
|
- `listActiveSessions()` - List all `task-*` tmux sessions
|
|
- `killSessionById(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`, `status` via `metadataCache.getFileCache()`
|
|
- Updates `session_active`, `last_session` via `fileManager.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: `osascript` for iTerm2 or Terminal.app
|
|
|
|
---
|
|
|
|
## Types Reference
|
|
|
|
### Settings (`types/settings.ts`)
|
|
|
|
```typescript
|
|
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`)
|
|
|
|
```typescript
|
|
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`)
|
|
|
|
```typescript
|
|
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:
|
|
|
|
```markdown
|
|
# Note: [Note Title 1]
|
|
|
|
[Full content of note 1]
|
|
|
|
# Note: [Note Title 2]
|
|
|
|
[Full content of note 2]
|
|
```
|
|
|
|
### Backlink Mapping
|
|
|
|
During file creation:
|
|
1. Register mapping: `"Original Title"` → `"sanitized-filename"`
|
|
2. When writing content, replace all `[[Original Title]]` with `[[sanitized-filename]]`
|
|
3. Ensures backlinks work despite filename sanitization
|
|
|
|
---
|
|
|
|
## Extending the Plugin
|
|
|
|
### Adding a New Command
|
|
|
|
1. Define command handler in `main.ts`
|
|
2. Register with `addCommand({ id, name, callback })`
|
|
3. Use existing services or create new ones
|
|
|
|
### Adding a New Service
|
|
|
|
1. Create file in `src/services/`
|
|
2. Export class with constructor accepting `App` and dependencies
|
|
3. Initialize in `main.ts` `initializeServices()`
|
|
4. Add to plugin class properties
|
|
|
|
### Adding Settings
|
|
|
|
1. Add property to `OpenAugiSettings` in `types/settings.ts`
|
|
2. Add default value to `DEFAULT_SETTINGS`
|
|
3. Add UI control in `ui/settings-tab.ts` `display()` method
|
|
|
|
### Adding a New Modal
|
|
|
|
1. Create file in `src/ui/`
|
|
2. Extend `Modal` from Obsidian
|
|
3. Implement `onOpen()` and `onClose()`
|
|
4. Use callback pattern for returning results
|
|
|
|
---
|
|
|
|
## Build & Development
|
|
|
|
```bash
|
|
npm run dev # Development build with watch
|
|
npm run build # Production build
|
|
npm run typecheck # TypeScript checking
|
|
```
|
|
|
|
### Publishing
|
|
|
|
See [PUBLISHING.md](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 class
|
|
- `App` - Vault and workspace access
|
|
- `TFile` - File abstraction
|
|
- `Modal` - Dialog windows
|
|
- `Notice` - Toast notifications
|
|
- `PluginSettingTab` - Settings panel
|
|
- `MetadataCache` - Link resolution
|
|
- `Vault` - File read/write operations
|
|
|
|
---
|
|
|
|
## External Dependencies
|
|
|
|
- **OpenAI API** - Chat completions endpoint
|
|
- **Dataview Plugin** (optional) - Query execution for advanced link discovery
|