Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
30fde53
feat: baseline obsidian plugin + dependency path normalization
paulbreuler Feb 13, 2026
5bcc188
feat(obsidian-plugin): add canvas/base surface audit and graph sync s…
paulbreuler Feb 13, 2026
cb79381
feat(obsidian-plugin): add directed graph view and resilient CLI inte…
paulbreuler Feb 13, 2026
1ce804f
fix(obsidian-plugin): improve health error UX and settings grouping
paulbreuler Feb 13, 2026
816b8fb
fix(obsidian-plugin): execute js limps binaries via node to avoid EACCES
paulbreuler Feb 13, 2026
6bdad70
fix(obsidian-plugin): avoid electron helper for js cli execution
paulbreuler Feb 13, 2026
49f0df3
feat(obsidian-plugin): expand graph to full vault with physics and ca…
paulbreuler Feb 13, 2026
0d1c904
feat(obsidian-plugin): move graph view to React with force-directed p…
paulbreuler Feb 13, 2026
33e8fa6
fix(obsidian-plugin): reconcile root links and suppress placeholder u…
paulbreuler Feb 13, 2026
2a0edac
fix(obsidian-plugin): keep force simulation live and reheat on drag
paulbreuler Feb 13, 2026
88d4897
feat(obsidian-plugin): add force/display controls and label-aware col…
paulbreuler Feb 13, 2026
4601016
feat(obsidian-plugin): add compact map controls and optional 3D force…
paulbreuler Feb 13, 2026
677dcbd
fix(obsidian-plugin): improve center/fit bounds behavior and simplify…
paulbreuler Feb 13, 2026
ea86567
feat(obsidian-plugin): refine edge semantics, add legend, and add sta…
paulbreuler Feb 13, 2026
be21ced
fix(obsidian-plugin): restore plan hierarchy grouping and stabilize 3…
paulbreuler Feb 13, 2026
0108c2d
feat(obsidian): improve graph visibility defaults and add day/night t…
paulbreuler Feb 13, 2026
3313688
fix(obsidian): restore 2D/3D layer switching visibility
paulbreuler Feb 13, 2026
2b79fca
feat(obsidian): default graph view to 2D, enable 3D on demand
paulbreuler Feb 13, 2026
6301a99
fix(obsidian): stop disconnected nodes from snapping back to center
paulbreuler Feb 13, 2026
6b3c5eb
feat(obsidian): make unresolved warnings clickable with file+line nav…
paulbreuler Feb 13, 2026
1a85e67
feat(obsidian): add graph close-to-hub flow and collapse warnings bel…
paulbreuler Feb 13, 2026
8e27ee5
feat(obsidian): improve graph legibility, custom icons, and theme nat…
paulbreuler Feb 13, 2026
135757a
chore(plans): checkpoint current LIMPS plan and agent updates
paulbreuler Feb 13, 2026
4495235
feat(obsidian): add editor diagnostics, protocol handler, and context…
paulbreuler Feb 14, 2026
85db725
chore(plans): clean up completed plans and create 0050-obsidian-deep-…
paulbreuler Feb 14, 2026
00d014c
feat(server): add ping tool, session renewal endpoint, and session in…
paulbreuler Feb 14, 2026
b1b70d7
feat(server): presence-aware indexing, smart exclusions, and streamin…
paulbreuler Feb 14, 2026
abb59fc
feat(server): document analysis, PageRank, concept extraction, and re…
paulbreuler Feb 14, 2026
b0d1eeb
feat(obsidian): persistent MCP client, tool wrapper, commands, modals…
paulbreuler Feb 14, 2026
fa441f6
docs: Obsidian deep integration architecture, plugin commands, and mu…
paulbreuler Feb 14, 2026
e261d90
fix(server): merge config exclusions into watcher ignore patterns
paulbreuler Feb 14, 2026
3aecd2c
feat(obsidian): auto-discover config and auto-start daemon
paulbreuler Feb 14, 2026
6d5a601
fix(obsidian): improve health view stability, fix scrolling and index…
paulbreuler Feb 17, 2026
d023e72
feat(obsidian): rewrite health view with React components
paulbreuler Feb 17, 2026
48018e7
fix(ci): address PR review comments and Node 20 test compatibility
paulbreuler Feb 17, 2026
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
10 changes: 9 additions & 1 deletion .claude/commands/create-feature-plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,9 @@ NNNN-descriptive-name/
title: [Descriptive Name]
status: GAP
persona: coder
depends_on: [000, 001] # zero-padded agent numbers this agent depends on; [] if none
depends_on:
- "./000_agent_infrastructure.agent.md"
- "./001_agent_testing_utilities.agent.md" # prefer relative markdown paths; [] if none
files: [src/path/to/file.ts]
tags: [feature-area]
---
Expand Down Expand Up @@ -250,6 +252,12 @@ Agent files must be self-contained for execution. Scoped references are allowed
when necessary (explicit file + heading), but broad searching means the distillation
is too thin.

## Dependency Format Preference

- Prefer `depends_on` entries as relative file paths to agent markdown files (for Obsidian graph visibility)
- LIMPS runtime supports both numeric IDs and file paths
- If numeric IDs exist, normalize with: `limps plan deps-to-paths`

## Work Type Adjustments

### Refactor
Expand Down
87 changes: 87 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,3 +123,90 @@ plans/
### Test Organization

Tests mirror the source structure in `packages/limps/tests/`. Tests run sequentially (`fileParallelism: false`) to avoid SQLite locking issues. Coverage threshold is 70% for all metrics.

## Development Workflow: Test-Driven Development (TDD)

Always follow test-driven development principles when working on this codebase:

### 1. Write Tests First
- Create test cases that describe the expected behavior
- Tests should clearly document what the code should do
- Use descriptive test names: `it('should return correct totalAgents count for PASS plans')`

### 2. Run Tests and Watch Them Fail
```bash
npm test -w packages/limps # Run all tests
npm run test:watch -w packages/limps # Watch mode for TDD
npx vitest run -t "test pattern" # Run specific test
```

### 3. Write Implementation Code
- Write minimal code to pass the tests
- Don't over-engineer; focus on passing tests
- Run tests frequently to validate changes

### 4. Refactor
- Once tests pass, improve code quality
- Ensure tests still pass after refactoring
- Remove duplication and improve clarity

### Example TDD Workflow

```bash
# 1. Create test file (e.g., packages/limps/tests/cli/tdd-issues.test.ts)
# 2. Write test cases describing the fix

# 3. Run tests (they will fail)
npm run test:watch -w packages/limps

# 4. Implement code to fix failing tests
# (e.g., fix getPlanStatusSummary to return totalAgents correctly)

# 5. Verify all tests pass
npm test -w packages/limps

# 6. Refactor if needed while keeping tests passing
```

## Code Style and Idioms

### Naming Conventions
- Use descriptive names that match business logic
- Functions: `getPlanStatusSummary()`, `renderPlanStatusCards()`
- Tests: `it('should return correct totalAgents for PASS plans')`
- Private methods: `_formatOutput()` or `private renderCards()`

### Error Handling
- Always wrap CLI operations in try-catch
- Return `{ ok: false, error: string }` for errors
- Use `CommandExecResult<T>` interface for CLI wrapper results
- Provide context-specific error messages

### JSON Output
All CLI commands with `--json` flag must:
```typescript
return {
success: true,
data: { /* actual data */ }
};
// or
return {
success: false,
error: "Description",
code?: "ERROR_CODE"
};
```

### TypeScript Best Practices
- Export interfaces for public APIs
- Use strict typing (no `any` except in rare cases)
- Type generic functions: `function run<T>(options: T): Promise<Result<T>>`
- Use discriminated unions for result types

### Testing Best Practices
- Each test file mirrors one source file (e.g., `src/cli/status.ts` → `tests/cli/status.test.ts`)
- Setup/teardown with `beforeEach` / `afterEach`
- Arrange-Act-Assert pattern in tests
- Shared test utilities in `tests/` root directory
- Mock external dependencies using vitest

21 changes: 16 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1179,13 +1179,24 @@ Add to config:

- `@sudosandwich/limps-headless` — Headless UI contract extraction, semantic analysis, and drift detection (Radix UI and Base UI migration).

## Obsidian Compatibility
## Obsidian Integration

limps works with Obsidian vaults. Open your `plans/` directory as a vault for visual editing:
The `@sudosandwich/limps-obsidian-plugin` package provides deep Obsidian integration:

- Full YAML frontmatter support
- Tag management (frontmatter and inline `#tag`)
- Automatic exclusion of `.obsidian/`, `.git/`, `node_modules/`
- **Persistent MCP session** to the limps daemon with auto-reconnect, keepalive, and CLI fallback
- **Document management** — create, update, delete plan documents from within Obsidian
- **Task management** — get next task, update task status (GAP/WIP/PASS/BLOCKED)
- **Search & proposals** — full-text search, proposal review, and auto-apply for safe fix types
- **Health Hub** — sidebar view with daemon, graph, link, and MCP status
- **Directed graph view** — interactive 2D/3D force graph with clickable nodes
- **Scheduled health checks** — periodic staleness, drift, and conflict detection
- **Vault automation** — auto-reindex when plan files change, event-driven refresh
- **Editor diagnostics** — inline link validation
- **Graph sync** to Obsidian surfaces (`.md`, `.canvas`, `.base`)

Full YAML frontmatter support, tag management (frontmatter and inline `#tag`), and automatic exclusion of `.obsidian/`, `.git/`, `node_modules/`.

See [`packages/limps-obsidian-plugin/README.md`](packages/limps-obsidian-plugin/README.md) for setup instructions, commands, and settings reference.

![Obsidian vault with limps plans](https://github.com/paulbreuler/limps/blob/main/.github/assets/obsidian-vault.png?raw=true)

Expand Down
140 changes: 140 additions & 0 deletions docs/obsidian-deep-integration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
# Obsidian Deep Integration Architecture

This document describes the architecture of the limps Obsidian plugin's deep integration layer: persistent MCP sessions, scheduled health checks, vault event automation, and the dual-mode execution model.

## MCP Session Lifecycle

The plugin uses `McpClientManager` to maintain a persistent MCP session to the limps HTTP daemon.

### Connection Flow

1. On plugin load, if `enablePersistentMcp` is `true`, the manager connects to the configured `mcpEndpoint` (default `http://127.0.0.1:4269/mcp`).
2. The manager sends a JSON-RPC `initialize` request with client info and protocol version.
3. On success, it stores the `mcp-session-id` header for subsequent requests.
4. A keepalive timer starts (default: every 14 minutes) sending periodic pings.

### Reconnection

When a request fails or the session is lost:

1. The manager emits a `disconnected` event with a reason string.
2. It enters reconnection mode, emitting `reconnecting` events with attempt counts.
3. Reconnection uses exponential backoff up to `mcpReconnectMaxAttempts` (default: 10).
4. On successful reconnect, a `connected` event is emitted with the new session ID.
5. If all attempts are exhausted, the manager stays disconnected and operations fall back to CLI.

### CLI Fallback

`LimpsToolWrapper` abstracts tool calls behind a unified interface:

- When MCP is connected, tool calls go through JSON-RPC (`tools/call`).
- When MCP is unavailable, the wrapper falls back to CLI execution via `execCommand()`.
- The wrapper exposes methods like `getProposals()`, `applyProposal()`, `searchDocs()`, `createDoc()`, `getNextTask()`, and `updateTaskStatus()`.

### Shutdown

On plugin unload:

1. The keepalive timer is cleared.
2. The MCP session is terminated (no explicit close request; the session times out server-side).
3. All event listeners are removed.

## Scheduled Health Checks

`WorkflowScheduler` runs periodic health checks against the limps daemon.

### Check Types

Each health check run performs:

1. **Daemon status** — verifies the daemon is running and reachable.
2. **Graph health** — queries graph health for entity/relation counts, conflicts, errors, and warnings.
3. Results are classified as `staleness`, `drift`, or `conflict` checks with issue counts.

### Configuration

| Setting | Default | Description |
|---------|---------|-------------|
| `enableScheduledHealthChecks` | `false` | Enable periodic checks |
| `healthCheckIntervalMs` | `14400000` (4 hours) | Interval between checks |

### Integration with Notifications

When issues are found and `notifyOnIssuesFound` is enabled, the `NotificationManager` shows Obsidian notices with severity-based timeouts:

| Severity | Timeout |
|----------|---------|
| `info` | 3 seconds |
| `success` | 4 seconds |
| `warning` | 7 seconds |
| `error` | 10 seconds |

The notification log retains up to 200 entries.

## Vault Event Automation

`VaultEventHandler` watches for vault changes and triggers automated responses.

### Event Filtering

The handler listens to Obsidian vault events (`modify`, `delete`, `rename`) and filters them:

1. Only `.md` files are considered.
2. Only files within the configured plans directory trigger actions.
3. The plans directory is computed as a relative prefix from the vault path (e.g., if vault is at `/vault` and plans are at `/vault/plans/`, the prefix is `plans/`).

### Auto-Reindex

When `autoReindexOnChange` is enabled, matching vault events trigger a debounced graph reindex via the CLI wrapper. The debounce interval is controlled by `eventRefreshDebounceMs` (default: 750ms).

### Auto-Proposals

When `autoApplyProposals` is enabled, the `runAutoProposals()` function:

1. Fetches current proposals via `LimpsToolWrapper.getProposals()`.
2. Filters to proposals whose `type` is in the `autoApplyProposalTypes` whitelist.
3. Applies each eligible proposal via `LimpsToolWrapper.applyProposal()`.
4. Reports results through `NotificationManager`.

Default whitelisted types: `frontmatter_cleanup`, `staleness_update`, `link_normalization`.

## File Watcher Behavior

The Obsidian plugin's vault event handler only watches files that pass the plans directory filter. This means:

- Files outside the plans directory (e.g., `.obsidian/`, other vault content) do not trigger reindex.
- Only markdown files trigger reindex — binary files, images, and other assets are ignored.
- The debounce ensures rapid successive saves produce a single reindex operation.

## Architecture Diagram

```
Obsidian Plugin (main.ts)
|
|-- McpClientManager (persistent MCP session)
| |-- JSON-RPC over HTTP to daemon
| |-- Keepalive timer
| |-- Reconnect with backoff
| +-- Event emitter (connected/disconnected/reconnecting/error)
|
|-- LimpsToolWrapper (dual-mode execution)
| |-- MCP path: callTool() -> JSON-RPC
| +-- CLI path: execCommand() -> child_process
|
|-- WorkflowScheduler (periodic health checks)
| |-- getDaemonStatus()
| +-- getGraphHealth()
|
|-- VaultEventHandler (vault automation)
| |-- Event filtering (plans dir + .md only)
| +-- Debounced reindex trigger
|
|-- NotificationManager (user notifications)
| |-- Severity-based Obsidian notices
| +-- In-memory log (max 200 entries)
|
+-- Auto-proposals (runAutoProposals)
|-- Fetch proposals
|-- Filter by whitelist
+-- Apply eligible proposals
```
Loading