Synthesized project .omp/RULES.md as a distinct sticky rule name so capability dedup no longer shadows it behind the user sticky rule.
Added a public rules capability regression test covering user and project sticky RULES.md files loading together.
Fixes#4739
Add a default-off "auto-learn" loop. When `autolearn.enabled` is set, after the
agent stops a session controller nudges it to capture reusable lessons: durable
facts go to long-term memory and repeatable procedures become "managed skills" —
SKILL.md files written to an isolated ~/.omp/agent/managed-skills directory that is
discovered and surfaced like authored skills but never overwrites them.
Two tools back this:
- `manage_skill` — create/update/delete managed skills.
- `learn` — record a lesson, optionally minting/enhancing a managed skill in the
same call (requires a hindsight/mnemopi memory backend).
The nudge is passive by default (a hidden reminder rides the next turn);
`autolearn.autoContinue` instead auto-runs one capture turn at stop, and
`autolearn.minToolCalls` (default 5) gates trivial turns. Plan/goal-mode turns and
subagents are never nudged, and the controller re-checks the live setting at fire
time so a mid-session opt-out takes effect.
Isolation & precedence: managed skills are a separate lowest-priority discovery
provider, so an authored skill of the same name wins across every provider and
custom directory regardless of third-party toggles; a disabled higher-priority
authored skill can never hide a managed one, and managed never masks an enabled
authored skill. Managed names and descriptions are sanitized on both write and
read (control/format chars, angle brackets, and Markdown fences) before they render
into the system prompt, and the SKILL.md byte cap is enforced on the final
serialized file.
Default off → zero footprint when disabled.
Store MCP OAuth credentials under deterministic mcp_oauth:<url> ids in each
profile's agent.db with refresh material embedded, so a definition-only entry
in a shared project mcp.json resolves each profile's own credential instead
of profiles clobbering each other's auth.credentialId pointer.
- Refresh material is single-source: embedded credential fields win over the
config auth block (which may belong to another profile); legacy rows fall
back to the auth block wholesale
- Wire the 401 refresh hook off the resolvable credential, not the auth
block, so definition-only bindings refresh mid-session too
- The url-keyed fallback never overrides a pinned Authorization header
- Send prompt=consent by default (oauth.prompt to override, "" to omit) so
reauth can switch accounts past an active browser session
- /mcp reauth fails fast on stdio transports (with an mcp-remote ~/.mcp-auth
hint), probes http/sse without OAuth injection, GCs the superseded legacy
row only after the flow succeeds, and leaves definition-only entries
untouched on disk
- DCR-issued client secrets stay embedded in the stored credential and are
never written into config files; user-supplied secrets survive reauth
Native user-level config discovery (MCP, skills, rules, slash commands, prompts, instructions, hooks, tools, settings, extensions, and the top-level SYSTEM.md/RULES.md/AGENTS.md) now resolves the user scope through getAgentDir() in builtin.ts, omp-extension-roots.ts, and the discovery-layer getUserPath() helper. A named profile sees only its own ~/.omp/profiles/<name>/agent config instead of the default profile's ~/.omp/agent leaking into every profile, matching the /mcp config writer and getMCPConfigPath("user").
discoverExtensionModulePaths now detects top-level symlinked directories that the native glob skips (follow_links=false) and synthesizes their index/package.json entry-point matches, so an extension shared across profiles via a symlink loads like a real directory. Symlinked extension files were already handled.
cli: check --tiny-worker on the profile-flag-stripped resolvedArgv, matching the adjacent --smoke-test check and launch routing.
The native rule discovery provider only scanned the rules/ subdirectory under .omp/ and ~/.omp/agent/. The documented top-level RULES.md file (per https://omp.sh/docs/context-files) was silently ignored — neither ~/.omp/agent/RULES.md nor <repo>/.omp/RULES.md was ever read.
Load both as synthetic rules with alwaysApply forced to true (the whole point of RULES.md is to be reattached every turn). Project scope walks up from cwd to repoRoot using the same nearest-.omp/ search AGENTS.md uses.
Fixes#1266
- Extracted prompt rendering and formatting utilities from coding-agent to centralized pi-utils package with new API surface (prompt.render, prompt.format, prompt.registerHelper).
- Migrated parseFrontmatter utility from coding-agent to pi-utils package; updated 8 files to import from @oh-my-pi/pi-utils.
- Removed 170-line prompt-format.ts module and consolidated 192 lines of Handlebars helper registrations into pi-utils prompt module.
- Updated 60+ files across coding-agent and typescript-edit-benchmark to use new prompt.render() and prompt.format() API from pi-utils.
- Simplified prompt-templates.ts by delegating core functionality to pi-utils while retaining custom helper registrations (jtdToTypeScript, jsonStringify, etc.).
* feat: monorepo-friendly discovery for AGENTS.md and skills
Support hierarchical config discovery in monorepos by walking up from
cwd through ancestor directories, combining files from all levels
instead of only checking the immediate working directory.
AGENTS.md context files:
- Change dedup key from file.level to file.level + depth, so context
files at different directory levels coexist instead of shadowing
- Root AGENTS.md and sub-project AGENTS.md are both included in the
system prompt, ordered from least specific to most specific
Skills:
- Modify all 4 providers with project-level skill directories (native,
claude, codex, opencode) to walk up from cwd through ancestors,
scanning for skills at each level
- Skills at closer directories win on name conflicts via existing
name-based dedup
Repo root boundary:
- Add findRepoRoot() utility that detects .git to identify repo root
- Add repoRoot field to LoadContext, computed once in loadCapability()
- All walk-up traversals (AGENTS.md, skills, nearest config dir) stop
at the repo root, preventing discovery from leaking outside the repo
- When not in a git repo, falls back to walking to filesystem root
Files changed:
- capability/fs.ts: add findRepoRoot()
- capability/types.ts: add repoRoot to LoadContext
- capability/index.ts: compute repoRoot in loadCapability()
- capability/context-file.ts: depth-aware dedup key
- discovery/agents-md.ts: bound walk-up at repo root
- discovery/builtin.ts: getAncestorDirs stopAt param, skill walk-up
- discovery/claude.ts: skill walk-up with repo root bound
- discovery/codex.ts: skill walk-up with repo root bound
- discovery/opencode.ts: skill walk-up with repo root bound
- extensibility/skills.ts: add repoRoot to inline LoadContext
* feat(agents-provider): add project-level discovery with ancestor walk-up for all capability types
The agents provider (.agent/.agents directories) previously only loaded
capabilities from the user home directory (~/.agent/, ~/.agents/). This
meant project-level .agents/ directories in monorepo roots were not
discovered when sessions started from subdirectories.
Add getProjectPathCandidates() helper that walks from cwd up to repoRoot,
scanning both .agent/ and .agents/ at each ancestor level. Apply this to
all six capability types: skills, rules, prompts, commands, context files
(AGENTS.md), and system prompts (SYSTEM.md). This matches the ancestor
walk-up behavior already present in the builtin (.omp), claude, codex,
and opencode providers.
All loaders now parallelize project-level and user-level scans via
Promise.all. Project-level results appear closest-first so dedup at the
capability layer picks the nearest override.
* fix(discovery): remove hard cap of 20 on ancestor directory walking
All walk-up loops already terminate naturally at repoRoot or filesystem
root. The depth < 20 / .slice(0, 20) / MAX_DEPTH caps were redundant
safety guards that would silently stop discovery in deeply nested
projects.
Removed from: agents.ts, agents-md.ts, builtin.ts, claude.ts, codex.ts,
opencode.ts, and corresponding test files.
* fix(agents-provider): set depth on project-level ContextFile entries for correct dedup
loadContextFiles was creating project-level ContextFile entries without
a depth field. Since contextFileCapability.key uses
`project:${file.depth ?? 0}`, all ancestor-level AGENTS.md files
collapsed to the same key and only the first survived dedup.
Compute depth via calculateDepth(cwd, ancestorDir) where ancestorDir is
two levels up from the file path (past the .agent/.agents config dir).
This gives each ancestor level a distinct dedup key.
* fix(discovery): stop ancestor walk-up at $HOME when not in a git repo
When repoRoot is null (no .git found), walk-up loops traversed all the
way to filesystem root. This caused $HOME to be scanned as a project-
level directory and then again as user-level, producing duplicates.
All providers now use `ctx.repoRoot ?? ctx.home` as the stop boundary:
stop at repoRoot if in a repo, otherwise stop at home. Applied to
agents.ts, agents-md.ts, builtin.ts, claude.ts, codex.ts, and
opencode.ts.
* fix(context-files): clamp depth >= 0 in dedup key and fix provider depth computations
The dedup key `project:${file.depth}` used raw depth, which could be
negative when providers computed it from config subdirectories (e.g.
.claude/, .github/, .gemini/) rather than the ancestor directory. This
caused same-scope cwd-level files to get distinct keys like project:-1
and project:0, bypassing dedup and injecting conflicting instructions.
Two-layer fix:
1. Key function: clamp to Math.max(0, depth) so any file at or below
cwd is treated as cwd-scope (depth 0). Defensive against future
providers.
2. Providers: fix root cause in claude.ts, gemini.ts, github.ts to
compute depth from the ancestor directory (parent of the config
subdir), not the config subdir itself.
---------
Co-authored-by: Can Bölük <can1357@users.noreply.github.com>
Add an optional `oauth` config block on MCP server entries in mcp.json,
allowing explicit `clientId` and `callbackPort` values for servers that
don't expose these through auto-discovery (e.g. Slack).
The `oauth.clientId` is used as a fallback — if the server's error
response or well-known metadata includes a client_id, that takes
precedence. The `callbackPort` defaults to 3000 when not specified.
- Consolidated @oh-my-pi/pi-utils subpath imports into single package root import across 100+ files.
- Moved tryParseJson utility from local web scrapers module to @oh-my-pi/pi-utils package for centralized JSON parsing.
- Renamed loadSkillsFromDir to scanSkillsFromDir and refactored skill discovery to use fs.promises.readdir instead of glob-based approach.
- Replaced custom parseJSON with tryParseJson across discovery modules for consistent error handling.
- Removed emitCustomToolSessionEvent method and cleanupSshResources function, consolidating shutdown logic into dispose method.
- Updated glob pattern construction to use GlobBuilder with literal_separator(true) for improved path handling.
- Added fallback empty string for undefined tool descriptions across all AI providers to prevent runtime errors.
- Made `description` field required in CustomTool interface and normalized tool descriptions in agent-loop.
- Refactored tool normalization in agent-loop by renaming `injectIntentIntoTools()` to `normalizeTools()` with conditional intent injection.
- Added comprehensive description metadata to todo-write tool schema fields for improved clarity.
- Added docs:// internal URL protocol handler for accessing embedded markdown documentation files with path traversal validation.
- Added support for executable tool files (.ts, .js, .sh, .bash, .py) in custom tools discovery from .omp/tools/ and .claude/tools/ directories.
- Added generate-docs-index build script to automatically scan and embed markdown documentation files at build time.
- Updated system prompt to document docs:// protocol usage and identify agent as operating within Oh My Pi harness.
- Added scoped TTSR rule matching with condition and scope fields supporting file globs and tool-specific filtering.
- Added ttsr.interruptMode setting to control when TTSR rules interrupt agent responses (never/prose-only/tool-only/always).
- Added support for loading rules, prompts, and commands from ~/.agent/ directory with fallback to ~/.agents/.
- Refactored rule discovery across all providers to use unified buildRuleFromMarkdown helper and per-stream-key buffering.
- Enhanced TTSR pattern matching to respect tool-specific scope filters and normalize file paths in glob matching.
- Removed support for `.pi` configuration directory alias in favor of `.omp` across all packages.
- Updated all configuration paths and references from `.pi` to `.omp` in user and project directories.
- Refactored configuration discovery in builtin.ts to simplify directory traversal logic and remove multi-alias support.
- Simplified Python module discovery to use single directory paths instead of arrays of aliases.
- Updated debug and crash log paths from `.pi/agent/` to `.omp/agent/` in TUI package.
- Removed unsafe OAuth endpoint extraction from error message text
- Fixed PKCE verifier storage with typed #codeVerifier field
- Fixed refresh token fallback using access token as refresh token
- Enforced restrictive file permissions (0o700/0o600) for MCP configs
- Fixed wizard buildConfig() to respect user-chosen env var and header names
- Fixed reauth endpoint discovery for non-OAuth servers
- Stored original config on connection, resolved config only for transport
- Added runtime type validation for enabled/timeout in config loaders
- Converted all TS private keywords to ES # private fields
- Wrapped uncaught throws in /mcp add with try/catch error handling
- Replaced new Promise with Promise.withResolvers() pattern
- Sanitized TUI output with replaceTabs/truncateToWidth
- Enforced http/https URL validation in add wizard
- Fixed greedy /mcp prefix match in input controller
- Corrected config filename references in MCP guide
- Added server name validation to updateMCPServer
- Fixed timeout timer leak in stdio transport
* + /mcp
- Reloads MCP manager in runtime state (no restart needed) and syncs with mcp.json.
- Handles OAuth discovery/auth flow automatically for auth-required servers.
- Validates server names and config shape before saving.
- Persists OAuth credentials in auth storage and links them to MCP config.
- Provides immediate connection checks and clear status messages.
- Supports enable/disable, reauth, and unauth flows that are easy to get wrong by hand.
* active agent tool registry runtime reload + token support for bearer auth http based transport
* +session rebind on succesful connection
* fix(coding-agent): address /mcp check failures
---------
Co-authored-by: can1357 <me@can.ac>
- Removed Prettier configuration files (.prettierignore and .prettierrc) and migrated formatting to Biome.
- Updated Biome configuration from version 2.3.11 to 2.3.12 and changed arrowParentheses rule from 'always' to 'asNeeded'.
- Pinned @biomejs/biome dependency to exact version 2.3.12 in package.json and bun.lock.
- Applied consistent arrow function formatting across 489 files by removing unnecessary parentheses around single parameters.
- Removed blank lines after comment blocks and reorganized imports for consistency across the codebase.
- Converted named imports from node modules (fs, path, os) to namespace imports across all packages.
- Extended extension loader error handling with isEacces and hasFsCode type guards.
- Removed WASM generation script; use Bun `wasm?raw` loader for imports.
- Added bunfig.toml with loaders for `.md`, `.py`, and `.wasm?raw` text imports.
- Added types/assets/index.d.ts for global TypeScript module declarations.
- Unified TypeScript configuration with tsgo-based checking across monorepo.
- Removed build and WASM steps from install and publish pipelines.
- Added tsconfig.publish.json files to all packages with optimized publish-time configuration.
- Updated all package.json scripts with prepublishOnly hooks for correct type checking during publish.
- Added @oh-my-pi/omp-stats path mappings to root tsconfig.json for consistent imports.
- Added WASM generation script for photon module and integrated into install:dev script.
- converted relative imports to path aliases ($c/*, $ai/*, $tui/*, etc.) across all packages
- added per-package tsconfig.json with complete path mappings for runtime resolution
- set importModuleSpecifier to non-relative for IDE auto-import preferences
- updated dev script to run from monorepo root for consistent path resolution
- Added FrontmatterError custom error class and FrontmatterOptions interface for improved error handling.
- Enhanced parseFrontmatter function with source, fallback, normalize, and fatal options for better error context.
- Updated all parseFrontmatter call sites to include source file information for better error messages.
- Fixed frontmatter parsing to properly report source location when YAML parsing fails.
- Updated system prompt to show file paths more clearly in output.
- Added template sections for systemPromptCustomization, environment, and tools in system prompt.
- Added todo_write tool for creating and managing structured task lists during coding sessions.
- Added persistent todo panel above the editor that displays task progress with Ctrl+T toggle.
- Added grouped display for consecutive Read tool calls in a compact tree view.
- Changed system prompt to use more direct, field-oriented language with emphasis on verification and assumptions.
- Changed Ctrl+Enter to insert newline when not streaming, and Ctrl+T from toggling thinking blocks to toggling todo list.
- Replaced yaml package dependency with Bun's built-in YAML parser and extracted frontmatter parsing to dedicated module.
- Converted SettingsManager.create(), loadSettings(), discoverSkills(), loadSlashCommands(), buildSystemPrompt(), loadSkills(), loadProjectContextFiles(), and getShellConfig() from synchronous to asynchronous APIs.
- Changed capability provider load() method from supporting both sync and async to async-only by removing loadSync().
- Removed fs property from LoadContext interface in capability types.
- Added new fs.ts module with caching layer for filesystem operations using readFile, readDirEntries, readDir, walkUp, and cache management functions.
- Refactored all discovery providers (builtin, claude, cline, codex, cursor, gemini, github, mcp-json, ssh, vscode, windsurf) to use async file operations with Promise.all for parallel loading.
- Extracted skill loading logic into reusable `loadSkillsFromDir` helper function to eliminate code duplication across builtin, claude, and codex discovery providers.
- Refactored builtin provider to use new `loadSkillsFromDir` helper instead of inline `loadSkillsRecursive` function.
- Refactored claude provider to use new `loadSkillsFromDir` helper, reducing function from 78 lines to 25 lines.
- Refactored codex provider to use new `loadSkillsFromDir` helper, consolidating duplicate skill transformation and frontmatter parsing logic.
- Updated extension module discovery to skip `node_modules` directories in addition to hidden directories.
- Added guideline to Task tool documentation about isolating file scopes for concurrent agents to prevent conflicts.
- Added Time Traveling Streamed Rules (TTSR) feature for real-time pattern matching and rule injection during streaming.
- Added TtsrManager with configurable settings for context mode, repeat mode, and repeat gap.
- Added TTSR trigger support across all rule discovery sources (builtin, cline, cursor, windsurf).
- Added abort signal handling in agent loop to support stream interruption for TTSR.
- Added TtsrNotificationComponent for displaying rule violation notifications in interactive mode.
- Added unified capability-based discovery system for loading configuration from 8 AI coding tools (Cursor, Windsurf, Cline, GitHub Copilot, Gemini, Codex, Claude, VS Code).
- Added Discovery settings tab in interactive mode for enabling/disabling configuration providers.
- Added provider source attribution showing which tool contributed each configuration item.
- Added support for tool-specific rule formats including Cursor MDC, Windsurf global_rules, and Cline .clinerules.
- Changed MCP tool name parsing to use last underscore separator for improved server name handling.
- Refactored core loaders (skills, hooks, custom tools, slash commands) to use capability API instead of manual directory scanning.