Commit Graph
41 Commits
Author SHA1 Message Date
can1357 8cd23ebd15 merge #3844: 3-way dirty-context fallback for isolated branch merges
# Conflicts:
#	packages/coding-agent/src/task/worktree.ts
#	packages/coding-agent/test/task/worktree.test.ts
2026-06-30 03:03:43 +02:00
roboomp d120ba6b7d fix(coding-agent): filter baseline wip from preserved agent commits
Dirty isolated baselines can be accidentally committed by subagents that run git add -A. Fetching the raw isolation HEAD then cherry-picking the range would replay that baseline WIP into parent history.

Add a dirty-baseline replay path that rewrites each agent commit against the captured baseline tree, preserving the agent commit message and author while excluding staged, unstaged, and untracked changes that existed before isolation started. Clean baselines still use the raw git fetch path, and nested-only changes keep returning patches without creating an empty root branch.

Add a regression for baseline staged + untracked WIP committed by the agent, asserting the task branch contains only the agent file and parent WIP remains staged/untracked after merge.

Fixes #3842
2026-06-30 00:28:27 +00:00
roboomp da715aae7c fix(coding-agent): preserve agent commits across isolated branch merges
When an isolated task agent commits its own changes before yielding, the
harness used to collapse the captured delta into one AI-summarized commit
and discard the agent's commit messages and authorship entirely. This
violated commit discipline for agentic swarms — multiple logical commits
("fix bug" + "add test") became a single opaque commit, and the
agent's commit object (which lived in isolation/.git/objects under
overlayfs/rcopy) was lost when cleanupIsolation tore down the overlay.

commitToBranch now detects when isolation HEAD moved past baseline.root
.headCommit. When it has, the function git-fetches the agent's HEAD into
the parent repo as omp/task/${taskId} so the commit objects survive
cleanupIsolation, and stamps the captured baselineSha onto the returned
CommitToBranchResult. mergeTaskBranches cherry-picks the inclusive range
baseSha..branchName when baseSha is provided, replaying each agent
commit verbatim with its original message and author. Any uncommitted
leftover (staged, unstaged, untracked) on top of the agent's last commit
becomes one trailing AI-summarized commit on the same branch.

Falls back to the legacy single-commit path when the agent never moved
HEAD (purely dirty working tree); existing patch-mode flow is untouched.

Fixes #3842
2026-06-30 00:13:07 +00:00
roboomp 4b98211c64 fix(coding-agent): fixed dirty isolated branch merges
Applied isolated branch patches with three-way fallback when unrelated parent dirt appears in patch context.

Surfaced branch preparation failures instead of reporting no changes.

Fixes #3841
2026-06-30 00:09:34 +00:00
roboomp 58c0a305d6 fix(agent): shortened isolated task paths
Used compact hashed isolation directory segments and the short m mount dir so long task ids are not copied into subagent working paths.

Kept worktree cleanup compatible with legacy merged task-isolation directories.

Fixes #3756
2026-06-28 21:25:20 +00:00
can1357 577d2a8eb8 style: biome format/organize-imports across integrated PRs 2026-06-27 02:06:38 +02:00
can1357 29269d9547 Fix eval agent isolation artifact recovery 2026-06-27 01:39:35 +02:00
roboompandcan1357 5b6e9f904d fix(eval): paused timeout over baseline capture and surfaced nested stash-restore failures
Two Codex P2 findings landed against 978d2a76d0 that were not in the previously delivered review event:

1) prepareIsolationContext() (which runs captureBaseline → walks nested repos and untracked diffs) was running OUTSIDE withBridgeTimeoutPause; on dirty/large repos the baseline walk can exceed the eval idle timeout while the runtime is blocked. Moved the prep call into the pause closure so the watchdog is suspended for the whole bridge call from prep through cleanup.

2) applyNestedPatches() swallowed git stash pop failures with only a logger.warn, so a stash-pop conflict after a successful agent commit was invisible to the workflow. Changed the helper to return Promise<string[]> of warnings; applyEligibleNestedPatches now wraps them in a <system-notification> appended to the merge summary so the caller actually sees the partial-success case.

Added regression tests:
- bridge: prepare fires after timeout-pause and before timeout-resume.
- runner: applyEligibleNestedPatches surfaces stash-restore warnings as a system-notification.
- worktree (real git): a pre-existing dirty edit on the same file the agent patches causes stash pop to conflict; the helper returns a warning naming the nested repo and the stash entry is preserved for manual recovery.

Fixes #3196
2026-06-22 21:57:35 +02:00
can1357 2f2acaf928 Merge pull request #3205: feat(eval): isolated/apply/merge options for agent() helper
Resolves conflict in test/task/worktree.test.ts by keeping both the
getRepoRoot (main) and applyNestedPatches (PR) describe blocks.

Extends the PR's Python/JS work to the remaining workflow runtimes:
- eval/rb/prelude.rb, eval/jl/prelude.jl: agent() now accepts and
  forwards isolated/apply/merge (as booleans) plus returnHandle, and the
  return_handle node carries isolated/patch_path/branch_name/
  nested_patches/changes_applied/isolation_summary.

Post-merge fixups:
- task/index.ts: drop dead commitStyle var (the dedup refactor reads
  task.isolation.commits inside makeIsolationCommitMessage).
- CHANGELOG: move the misplaced Added entry under [Unreleased], correct
  the stale "defaults track task.isolation.mode" wording to the final
  strict opt-in behavior, and note all four runtimes.

Fixes #3196
2026-06-22 21:56:45 +02:00
roboomp abc9a8f292 fix(task): restored nested stash with index state
git stash pop without --index restores stashed staged changes as unstaged. When a nested repo had staged WIP before the isolated agent ran, the pop in applyNestedPatches() brought the content back but lost the user's index state.

Pass { index: true } so pop uses --index, matching the root merge path that already does the same thing.

Added a regression test that stages a pre-existing edit in the nested repo, runs applyNestedPatches, and asserts the file is still in the index (porcelain "M  " with the trailing space) and the cached diff still shows the staged WIP.

Fixes #3196
2026-06-22 19:41:59 +00:00
roboomp 978d2a76d0 fix(task): preserved nested-repo dirty state across nested patch apply
applyNestedPatches() applied the captured patch then ran git.stage.files(nestedDir), which stages every working-tree change in the nested repo. A nested repo that was already dirty before the agent ran ended up with the user's unrelated work-in-progress committed alongside the agent delta.

Stash any pre-existing dirty state (tracked + untracked) before applying the patch and pop it back in the finally block after the commit, so the agent commit contains only the captured patch and the user's in-flight work is restored on top of it. A failing stash pop logs a warning and leaves the stash entry intact for manual recovery; the broader nested-apply failure path is already non-fatal.

Added a worktree integration test that confirms a pre-existing untracked file in the nested repo is not staged into the agent commit and is still present in the working tree afterwards.

Fixes #3196
2026-06-22 19:34:42 +00:00
can1357 f353ae0067 chore: biome format after PR integration 2026-06-21 17:20:22 +02:00
can1357 e8b60408b2 Merge PR #1939: fix(coding-agent): guard Git-mutating automation in pure jj workspaces (@roboomp)
# Conflicts:
#	packages/coding-agent/src/task/worktree.ts
#	packages/coding-agent/test/task/worktree.test.ts
2026-06-21 17:05:15 +02:00
can1357 cd8409154d fix(coding-agent): serialized isolated-task merges and fixed async batch accounting
stash/cherry-pick merge sequence runs under the repo lock (lost-uncommitted-changes race); stash-pop failure no longer mislabels merged branches; async batches cannot stick at running forever; queued tasks stop counting against the global job cap; aborted session startup disposes the late session; fail-fast propagates the worker signal; command expansion treats user input dollar-patterns literally; progress snapshots stop structured-cloning tool payloads.
2026-06-10 01:27:40 +02:00
roboomp d7c7d2b0e1 fix(coding-agent): guarded nested pure jj before outer-git fallback
Previously `getRepoRoot` and `ensureAutoresearchBranch` only consulted `jj.isPureJjRepo` after `git.repo.root` returned null. For a jj workspace nested under an unrelated outer Git checkout, `git.repo.root` walks up and finds the outer .git, so the pure-jj branch never fired and isolation/autoresearch silently mutated the surrounding Git tree behind jj's back.

Both call sites now run the pure-jj guard first, rejecting at the jj workspace's own root regardless of any surrounding Git checkout. Added integration tests for the nested case on both surfaces.

Refs #1935
2026-06-05 14:44:05 +00:00
roboomp 7ba2227c3c fix(coding-agent): guarded Git-mutating automation in pure jj workspaces
Worktree setup and autoresearch Git prep silently misbehaved in pure Jujutsu workspaces (`.jj/repo/` present, no colocated `.git/`):

- `task/worktree.ts#getRepoRoot` threw a generic "Git repository not found for isolated task execution.", giving a jj user no hint about what was wrong.

- `autoresearch/git.ts#ensureAutoresearchBranch` returned a soft "Not in a git repository" warning, letting `/autoresearch` proceed with no branch isolation, baseline reset, or auto-commits.

Both paths now detect a pure jj workspace via a new `jj.isPureJjRepo` helper and surface an actionable Jujutsu-specific error pointing at `jj git init --colocate`. Colocated jj-git workspaces (both `.jj/` and `.git/` at the same root) and plain Git checkouts behave exactly as before.

Fixes #1935
2026-06-05 14:39:39 +00:00
can1357 1228c96959 feat(coding-agent): added worktree list/clear CLI with orphan pruning
- Added the new `omp worktree` (`wt`) command with `list|clear`, `all/dry-run/json` options, and CLI registration.
- Added `listWorktrees`/`clearWorktrees` flows that scan worktrees, classify orphaned entries, emit JSON, and call `worktree.prune`.
- Replaced legacy path encoding with `hashPath` via `getWorktreeDir`, updating task isolation, storage keys, and PR checkout paths.
- Added bounded PR worktree path retries before `git worktree add` and updated checkout-path tests for hashed names.
2026-05-22 12:47:13 +09:00
can1357 1cffb3473a fix: corrected worktree baseline capture with synthetic tree diff
- Added untracked worktree baseline capture via `untrackedPatch` and synthetic tree diffing.
- Added fallback-aware backend ordering by collecting host candidates and retrying alternates on unavailable PAL.
- Hardened overlay mount lifecycle by removing stale overlays and deleting upper/work dirs after unmount.
- Refined ZFS clone deletion to validate ownership and remove dataset+origin only when checks pass.
- Updated ProjFS integration to use extended-info callbacks and symlink metadata reads.
- Updated rcopy path handling to absolutize paths, and added `writeTree` plus combined shell timeout checks.
2026-05-12 14:36:31 +02:00
can1357 d37d48d11d feat(pi-iso): added unified pi-iso backend resolver with auto probe
- Added unified isolation primitives (BackendKind, ProbeResult, IsoError) and resolve fallback selection logic.
- Added Diff, FileChange, and ChangeKind with default_diff choosing git mode or filesystem walk based on repository state.
- Added APFS/btrfs/zfs/reflink/overlayfs/projfs/rcopy/block-clone backends with platform-aware start, stop, and probe.
- Mapped backend operations to canonicalized paths, recursive clone helpers, rollback cleanup, and unavailable error mapping.
- Added unified native exports isoBackend/isoProbe/isoResolve/isoStart/isoStop/isoDiff and removed projfsOverlay APIs.
- Replaced task isolation resolver flow with ensureIsolation/cleanupIsolation and migrated mode handling to auto plus legacy-mode aliases.
2026-05-12 13:39:45 +02:00
can1357 6d07944654 refactor: migrated binary detection to $which() utility across codebase
- Replaced all Bun.which() calls with $which() utility from @oh-my-pi/pi-utils across 22 files.
- Removed findBashOnPath() wrapper function from procmgr.ts, consolidating binary path resolution.
- Updated AGENTS.md documentation to reflect new $which() API usage pattern.
- Centralized binary detection logic through shared utility, reducing code duplication.
2026-04-08 05:28:22 +02:00
can1357 3d29a48e7a fix(coding-agent): fixed memory leak by cancelling idle compaction
- Fixed memory leak by cancelling idle compaction timer on event controller disposal.
- Fixed session resumption to preserve last non-empty session when starting fresh.
- Fixed stash detection to use git ref resolution instead of output parsing for reliability.
- Fixed secret obfuscation to deobfuscate restored session messages locally while keeping LLM messages obfuscated.
- Fixed stash pop operation to preserve staged changes with --index flag after task branch merges.
- Changed idle compaction settings from enum to numeric type for flexible configuration.
2026-04-05 03:50:52 +02:00
can1357 287809c5cc fix: treat stash-pop conflicts as merge failures to preserve recovery branches 2026-04-05 01:16:16 +02:00
DeprecatedLuke d64fa7e859 feat(coding-agent): preserve results on isolated merge failures 2026-04-04 11:52:58 +01:00
can1357 418b6801a0 refactor(coding-agent): migrated git operations to centralized utils module
- Extracted git operations from ControlledGit class into centralized utils/git module with 1276 lines of typed command wrappers.
- Replaced dependency injection of ControlledGit instances with direct cwd string parameters across commit agent tools and workflows.
- Migrated all git command execution from inline shell calls and custom helpers to structured git module API (diff, status, branch, worktree, patch, etc.).
- Removed ControlledGit class, operations.ts, and helper functions (findGitHeadPath, mergeStdoutStderr, joinPatch) now provided by git module.
- Exported git utilities from main package entry point for extension and plugin use.
2026-04-02 07:35:44 +02:00
801553b118 Implement fuse-projfs as an alternative to fuse-overlay for Windows. (#244)
* Implement fuse-projfs to use ProjFS on WIndows.

* fix(pi-natives): prevent ProjFS session key mismatch and start race

---------

Co-authored-by: can1357 <me@can.ac>
2026-03-03 04:12:30 +01:00
Kevin LoftisandGitHub 5af63f755d fix: use --no-optional-locks for background git status calls (#188)
* fix: use --no-optional-locks for background git status calls

Prevent index.lock contention by adding --no-optional-locks to all
git status --porcelain calls. This is the same approach VSCode uses
in its built-in git extension (GIT_OPTIONAL_LOCKS=0).

The status-line component polls git status every ~1s to show
staged/unstaged/untracked counts. Without --no-optional-locks,
each call acquires index.lock for an opportunistic index refresh,
which blocks concurrent git operations (pull, rebase, commit) run
by the user or by omp's own tool execution.

The git-status documentation explicitly recommends this:
  'Scripts running status in the background should consider
   using git --no-optional-locks status'

References:
- git docs: https://git-scm.com/docs/git-status (BACKGROUND REFRESH)
- git commit adding the flag: https://github.com/git/git/commit/27344d6
- VSCode's fix: GIT_OPTIONAL_LOCKS=0 in extensions/git/src/git.ts:2716
- Claude Code same issue: https://github.com/anthropics/claude-code/issues/11005
- GitExtensions same issue: https://github.com/gitextensions/gitextensions/issues/5066
- VSCode issue #31069: https://github.com/microsoft/vscode/issues/31069

* style: fix biome formatting for long lines in worktree.ts
2026-02-27 11:59:39 +01:00
can1357 1c8b3f8cb3 Merge PR #155 and fix isolation merge regressions 2026-02-26 08:53:25 +01:00
DeprecatedLuke 7499379756 fix(task): make merge failures non-fatal, preserve agent output 2026-02-23 20:37:51 +00:00
DeprecatedLuke 39ff2ab6e7 feat(task): add task.eager setting for eager task delegation 2026-02-23 20:36:25 +00:00
can1357 a83175c94c refactor: migrated imports to unified package root and consolidated skill discovery logic
- 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.
2026-02-23 20:59:17 +01:00
DeprecatedLuke 696d6ba545 feat(task): add better commit messages and nested repo isolation 2026-02-23 19:17:43 +00:00
DeprecatedLuke c1370f7332 feat(task): implement branch merge strategy for isolated tasks 2026-02-23 19:17:42 +00:00
DeprecatedLuke 995b26b90f feat(task): add nested non-submodule repo support for isolation 2026-02-23 19:17:41 +00:00
DeprecatedLuke 86cd66b3d5 feat(task): add fuse-overlay isolation backend 2026-02-23 19:17:40 +00:00
can1357 fcd7ff4f84 refactor(dirs): centralized directory path utilities into @oh-my-pi/pi-utils/dirs module
- Extracted directory path utilities from multiple packages into a centralized '@oh-my-pi/pi-utils/dirs' module.
- Moved 30+ path helper functions (getAgentDir, getConfigRootDir, getPluginsDir, getMCPConfigPath, etc.) from scattered locations into a single shared utility module.
- Consolidated APP_NAME, CONFIG_DIR_NAME, and VERSION constants into the centralized dirs module for reuse across packages.
- Updated 70+ import statements across packages/ai, packages/coding-agent, packages/stats, and packages/tui to use the new centralized module.
- Removed local path construction logic and replaced with utility function calls for improved maintainability and consistency.
- Deleted packages/coding-agent/src/extensibility/plugins/paths.ts as its functions were moved to the centralized dirs module.
2026-02-13 15:06:05 +01:00
can1357 a5d9bbb4ca chore(coding-agent): migrated console logging to structured logger and updated import styles
- Migrated console.error() calls to structured logger.warn() and logger.error() throughout codebase.
- Updated os.tmpdir() import style in browser tool from destructured to namespace import.
2026-02-10 11:26:42 +01:00
can1357 b59b104bad refactor(idgen): replaced nanoid with Snowflake ID generator
- Replaced nanoid and randomUUID with Snowflake ID generator across codebase for consistent distributed ID generation.
- Created new Snowflake utility module in packages/utils with support for 64-bit distributed ID generation with configurable epoch, machine ID, and sequence numbers.
- Updated session ID format documentation from nanoid to snowflake hex string format (16 hex characters).
- Removed nanoid dependency from package.json as it is now replaced by internal Snowflake implementation.
2026-02-05 03:48:08 +01:00
can1357 779ca4872b style(deps): migrated from Prettier to Biome and updated formatting rules
- 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.
2026-01-24 04:20:19 +01:00
can1357 51d39e7eea refactor(imports): migrated node module imports to namespace imports
- 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.
2026-01-24 03:37:59 +01:00
can1357 e22d123009 refactor(fs): migrated remaining sync file operations to async
- Converted readdirSync, readFileSync, and statSync to async readdir, readFile, stat across skills and agent discovery.
- Made scanDirectoryForSkills async and refactored custom directory scanning to use Promise.all for concurrent processing.
- Updated agent discovery to use fs/promises for async file reading and refactored helper patterns.
- Added AgentParsingError exception class for better error handling during agent parsing.
- Added filesystem error type guards (isEnoent, isEacces, isPerm, etc.) to pi-utils for safe error checking.
- Added color manipulation utilities to pi-utils for accessibility features.
- Added color-blind mode setting to settings manager.
- Migrated plugins, settings, and config modules from sync to async file operations.
- Updated error handling to use new pi-utils type guards for type-safe checking.
2026-01-24 03:18:03 +01:00
can1357 cb5bbbf382 refactor(coding-agent): flattened directory structure, eliminated core/ folder
- Eliminated core/ directory (252 files, 23 subdirs → distributed)
- Reduced max nesting from 9 levels to 5 levels
- Promoted tool subdirs to top level: exa/, lsp/, patch/, task/, web/
- Merged web-scrapers/ + web-search/ into web/{scrapers,search}/
- Flattened modes/interactive/ to modes/
- Split execution/ into: ipy/ (python), exec/ (bash), ssh/
- Renamed ipy/python-*.ts to ipy/*.ts (executor, kernel, etc.)
- Flattened cursor/exec-bridge.ts to cursor.ts
- Created logical groupings: config/, session/, extensibility/, export/
- Updated all imports across 500+ files
2026-01-23 12:24:47 +01:00