Commit Graph

15 Commits

Author SHA1 Message Date
0xMomo
ef75f80d9d fix(daemon): clean stale agent branches during repo gc (MUL-2550) (#3039)
* fix(daemon): 清理陈旧 agent 分支

Co-authored-by: multica-agent <github@multica.ai>

* fix(daemon): 串行化 bare repo gc

Co-authored-by: multica-agent <github@multica.ai>

* test(daemon): adapt health repo cache mock

Co-authored-by: multica-agent <github@multica.ai>

* fix(daemon): gate gc maintenance on stale-branch deletion

Address review feedback on the bare-repo GC change:

- Only run `reflog expire` + `git gc --prune=30.days` when we actually
  deleted a stale agent branch this cycle. Previously the heavy step
  ran every GC tick on every cached repo even when there was nothing
  to reclaim, turning a stale-ref cleanup into a periodic full-repo
  maintenance job under the per-repo lock.
- Split git command timeouts: `gc --prune=30.days` now gets a
  10-minute budget instead of sharing the 30s ceiling that was scoped
  for the original `worktree prune` call. Light commands stay at 30s.
- Drop the redundant `gc --auto` — `gc --prune=30.days` already
  performs the maintenance `gc --auto` would have triggered.
- Narrow the agent-namespace ref query from `refs/heads/agent` to
  `refs/heads/agent/` so the pattern can't surface a literal
  `agent` branch outside the daemon namespace.

Tests:
- New TestPruneWorktree_IgnoresLiteralAgentBranch pins the trailing-
  slash narrowing.
- New TestPruneWorktree_SkipsMaintenanceWhenNothingDeleted uses an
  unreachable, backdated loose object as a sentinel to verify that
  `gc --prune` runs only when a stale agent branch was reaped.

Co-authored-by: multica-agent <github@multica.ai>

---------

Co-authored-by: 0xNini Code Dev <agent@multica.local>
Co-authored-by: multica-agent <github@multica.ai>
Co-authored-by: 0xNini <0xnini@iMac-Pro.local>
Co-authored-by: J <j@multica.ai>
2026-06-08 15:25:14 +08:00
Kagura
fb026f2607 fix(daemon): suppress git console windows on Windows (#2358)
* fix(daemon): suppress git console windows on Windows

Apply the same HideConsoleWindow pattern used for agent processes
(PR #1474) to all git commands spawned by the daemon's repo-cache,
execenv, and GC packages. Each exec.Command now calls
util.HideConsoleWindow(cmd) which sets CREATE_NEW_CONSOLE + HideWindow
so grandchildren inherit a hidden console instead of flashing visible
console windows.

Closes #2357

Co-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>

* refactor: use EnsureHiddenConsole at daemon startup

Replace per-site HideConsoleWindow(cmd) calls with a single
EnsureHiddenConsole() invoked once at daemon startup. The daemon
now owns a hidden console that every child process (git, cmd /c
mklink, etc.) inherits automatically, eliminating the need for
per-call SysProcAttr configuration.

This also covers the previously missed exec.Command in
codex_home_link_windows.go (cmd /c mklink) which never had a
HideConsoleWindow call.

Signed-off-by: kagura-agent <kagura.agent.ai@gmail.com>

---------

Signed-off-by: kagura-agent <kagura.agent.ai@gmail.com>
Co-authored-by: Claude Opus 4 (1M context) <noreply@anthropic.com>
2026-05-11 14:41:07 +08:00
Ákos Seres
64c605e227 fix(execenv): write OpenCode skills to .opencode/skills/ for native discovery (#2016)
* fix(execenv): write OpenCode skills to .opencode/skills/ for native discovery

* fix(repocache): exclude OpenCode skill directory
2026-05-06 11:48:06 +08:00
Bohan Jiang
3dc3e49a47 fix(daemon): remove Co-authored-by hook when workspace setting is off (#2035)
* fix(daemon): remove Co-authored-by hook when workspace setting is off

The prepare-commit-msg hook is installed in the bare repo's shared
hooks dir, so once installed it persists across worktrees. CreateWorktree
only installed the hook when the setting was enabled, but never removed
it — so disabling the workspace toggle had no effect on subsequent
commits.

Add removeCoAuthoredByHook and call it in both CreateWorktree branches
when the setting is disabled. Use a marker comment in the hook script so
removal only deletes hooks the daemon owns; user-installed hooks at the
same path are left alone.

Co-authored-by: multica-agent <github@multica.ai>

* fix(daemon): recognize legacy Multica prepare-commit-msg hook on removal

The first cut of removeCoAuthoredByHook only recognized hooks installed
by the new code (containing the multicaHookMarker sentinel). Bare clones
already on disk from previous daemon releases carry the older script
without that line, so toggling the workspace setting off would have
treated them as user hooks and left the trailer in place — exactly the
state reported in MUL-1704.

Match against a list of known daemon signatures (current marker + the
legacy "Installed by the Multica daemon." comment), and add a test that
seeds the verbatim legacy hook before CreateWorktree(... disabled) to
keep recognition aligned with what production hosts actually have on
disk.

Co-authored-by: multica-agent <github@multica.ai>

---------

Co-authored-by: multica-agent <github@multica.ai>
2026-05-03 21:09:16 +08:00
Bohan Jiang
a414a00b4a refactor(repocache): clarify resolveBaseRef comment and cover tag refs (#2023)
Follow-up nits from PR #1988 review:

- Move the comment that documents getRemoteDefaultBranch's resolution
  walk into the resolveBaseRef call site description, and rephrase the
  "" branch so it's clear that path only fires for the default-branch
  case (the requested-ref path returns an explicit error before
  reaching it).
- Add TestCreateWorktreeWithRequestedTagRef to lock in the
  refs/tags/<ref> candidate. The test tags the initial commit, advances
  the default branch past it, then asserts the worktree HEAD matches
  the tagged commit (so the tag must have been resolved, not the
  default branch).

Co-authored-by: multica-agent <github@multica.ai>
2026-05-03 11:30:25 +08:00
Prince Pal
862b0509df feat: support repo checkout ref selection (#1988) 2026-05-03 11:27:16 +08:00
Jiayuan Zhang
3df95c84b8 fix(daemon): add safe.directory=* to gitEnv to fix CI dubious ownership errors (#1980)
* fix(daemon): add safe.directory=* to gitEnv to fix CI dubious ownership errors

TestRegisterTaskReposAllowsProjectOnlyURL and
TestRegisterTaskReposSurvivesWorkspaceRefresh fail on GitHub Actions CI
because git clone --bare from local temp directories triggers git's
safe.directory ownership check when the runner UID differs from the
directory owner.

Set safe.directory=* via GIT_CONFIG env vars in gitEnv() so all daemon
git subprocesses trust any directory. The daemon manages its own bare
caches and worktrees, so the ownership check provides no security value.

Co-authored-by: multica-agent <github@multica.ai>

* fix(daemon): preserve existing GIT_CONFIG_* entries in gitEnv

Instead of resetting GIT_CONFIG_COUNT to 1, read the existing count
from the environment and append safe.directory at the next available
index. This preserves any env-scoped git config (auth, URL rewrites,
extra headers) injected into the daemon process.

Adds TestGitEnvPreservesExistingConfig to verify the append behavior.

Co-authored-by: multica-agent <github@multica.ai>

---------

Co-authored-by: multica-agent <github@multica.ai>
2026-05-01 16:18:58 +02:00
Bohan Jiang
da5dbc6224 refactor(repos): drop unused description + tighten create-project layout (#1930)
* refactor(repos): drop unused description + tighten create-project layout

Two related changes that touch the workspace-repos surface together.

1. Remove the per-repo `description` field everywhere it was threaded.
   The only place it ever surfaced was a markdown table column the daemon
   wrote into the agent runtime config, where most rows just read "—"
   anyway. Agents already discover project structure by running
   `multica project` / `multica issue` against the CLI, so the human-
   readable description string carried no real value while taking up an
   extra Settings input row and propagating through six layers (settings
   UI → workspace.repos jsonb → handler RepoData → daemon RepoData →
   repocache.RepoInfo → execenv.RepoContextForEnv).

   - Settings → Repositories drops the description input; the URL field
     now spans the whole row.
   - WorkspaceRepo TS type loses `description`; backend RepoData /
     RepoInfo / RepoContextForEnv all collapse to URL only.
   - Daemon's runtime_config Repositories block changes from a
     `| URL | Description |` markdown table to a simple bullet list.
   - Tests updated; jsonb residue in existing workspaces is dropped at
     normalize time, so no migration needed.

2. Tighten the Create Project modal footer: pull the Status / Priority /
   Lead / Repos pills onto the same row as the Create Project button
   (Linear-style single-row footer) instead of stacking them above it,
   and swap the Repos pill icon from `FolderGit` to a real GitHub mark
   (lucide-react v1 dropped brand icons, so the mark lives inline as a
   small SVG component in this file).

   I tried promoting Repos to its own "Resources" strip above the footer
   to separate the resources abstraction from project metadata, but with
   a single pill it looked too sparse — leaving a TODO comment in the
   footer to revisit once we add Linear / Notion / Figma / Slack
   resource types.

* fix(daemon test): drop residual Description field on RepoData literals

* fix(repos): drop Description residue surfaced after rebase on #1929

Project-resource github_repo lift path (#1929) and registerTaskRepos
both still constructed RepoData{...Description: ...} after the rebase.
Two test sites in daemon_test.go and execenv_test.go also reintroduced
the field. Strip them so the Description-removal change builds and
tests pass with the latest main.
2026-04-30 14:55:03 +08:00
Jiayuan Zhang
b6a3f8ed58 feat(daemon): add Co-authored-by trailer for Multica Agent to git commits (#1907)
* feat(daemon): add Co-authored-by trailer for Multica Agent to git commits

Install a prepare-commit-msg hook in worktree bare repos that appends
"Co-authored-by: multica-agent <github@multica.ai>" to every commit
made by agents. Uses git interpret-trailers for proper formatting and
skips duplicates.

* feat(settings): add Co-authored-by toggle in workspace Labs settings

Add a workspace-level toggle to enable/disable the Co-authored-by
trailer for agent commits. Default is enabled (on).

Backend:
- Include workspace settings in daemon register response
- Store settings in daemon workspaceState
- Thread CoAuthoredByEnabled through WorktreeParams to conditionally
  install the prepare-commit-msg hook
- Parse co_authored_by_enabled from workspace settings JSONB

Frontend:
- Replace empty Labs tab placeholder with a Git section containing
  a Switch toggle for the Co-authored-by trailer setting
- Optimistically update the workspace query cache on toggle

* chore(daemon): skip squash commits in Co-authored-by hook

Test commit to verify the prepare-commit-msg hook appends the
Co-authored-by trailer automatically.

Co-authored-by: multica-agent <github@multica.ai>

---------

Co-authored-by: multica-agent <github@multica.ai>
2026-04-29 23:02:50 +02:00
L.Amar
9c177562e2 fix(daemon/repocache): make bare repo cache keys collision-resistant 2026-04-24 13:04:08 +08:00
marcel
287a9eb546 fix(repocache): pass explicit env to remote-facing git subprocesses (#1029)
fix(repocache): pass explicit env to remote-facing git subprocesses
2026-04-15 13:15:36 +08:00
gyh1621
6e475b9521 fix(daemon/repocache): unstick stale cache from initial snapshot
The bare cache used a mirror-style fetch refspec
(+refs/heads/*:refs/heads/*) which collided with worktree-locked
refs/heads/agent/<task> branches once those branches were pushed
back to origin as PRs. git fetch aborted with "refusing to fetch
into branch ... checked out at ...", the error was swallowed as a
warning, and every subsequent checkout reused the snapshot from
the original clone.

Fix:
- Clone / migrate bare caches to a remote-tracking layout
  (+refs/heads/*:refs/remotes/origin/*) so fetched heads never
  land in refs/heads/*.
- Resolve the base ref from refs/remotes/origin/HEAD with a
  5-level fallback (verified origin/HEAD symref to origin/main
  or origin/master to the bare HEAD bridged into origin/<same>
  to single-entry origin/* scan to bare HEAD for legacy caches).
- Refuse to guess when refs/remotes/origin/* has multiple
  candidates and none match a known fallback, so CreateWorktree
  fails loudly instead of basing work on an arbitrary branch.
- Refresh refs/remotes/origin/HEAD after every successful fetch,
  not just on the legacy migration path, so a cache that was
  already modern picks up an upstream default-branch change.
- Verify the primary symref target actually exists so a phantom
  refs/remotes/origin/HEAD from a broken set-head does not
  surface a deleted branch.
- Detect legacy caches on the fly and rewrite refspec +
  refs/remotes/origin/* + refs/remotes/origin/HEAD in place so
  existing clones self-heal on next use.
- Serialize per-bare-repo mutation (both Sync and CreateWorktree)
  with sync.Map-backed mutexes so concurrent fetch and worktree
  add on the same repo cannot race on git's own lockfiles.
- Narrow the already-exists retry to actual branch-collision
  errors so a path-collision no longer silently leaks a branch
  into the bare repo.
2026-04-09 14:34:51 +08:00
LinYushen
7df5750979 fix(daemon): update existing worktree to latest remote on reuse (#489)
* fix(daemon): update existing worktree to latest remote on reuse

When an agent receives a new task on the same issue, the execution
environment is reused and the repo worktree already exists on disk.
Previously, `multica repo checkout` would fail because `git worktree add`
cannot create a path that already exists — so the agent worked on stale
code from the prior task.

Now `CreateWorktree` detects existing worktrees and updates them:
fetch origin, reset working tree, then checkout a new branch from the
latest remote default branch. The previous task's branch is preserved.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(daemon): propagate actual branch name and use correct ref in worktree reuse

- Return (string, error) from updateExistingWorktree so collision-retried
  branch name propagates to WorktreeResult
- Use baseRef directly instead of origin/baseRef — bare clone refspec maps
  remote branches to local refs, so remote-tracking refs may not exist
- Remove redundant fetch (worktree shares object store with bare clone)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 14:13:44 +08:00
Quake Wang
36db325d50 feat(daemon): add opencode as supported agent provider (#341)
* feat(daemon): add opencode as supported agent provider

Add opencode backend alongside claude and codex. The backend spawns
`opencode run --format json`, parses streaming JSON events (text,
tool_use, error, step_start/finish), and supports --prompt for system
prompts. Includes CLI detection, AGENTS.md runtime config, native skill
discovery via .config/opencode/skills/, and 21 tests covering handlers,
JSON parsing, and integration-level processEvents scenarios.

* chore: add .tool-versions to gitignore
2026-04-02 17:52:07 +08:00
Jiayuan
cdc1ac708e feat(daemon): agent-driven repo checkout with bare clone cache
Agents now decide which repo to use based on issue context and check out
repos on demand via `multica repo checkout <url>`. Workspace repos are
cached locally as bare clones for fast worktree creation.

Key changes:
- Add repocache package for bare clone management (clone, fetch, worktree)
- Add `multica repo checkout` CLI command that talks to local daemon
- Add POST /repo/checkout endpoint on daemon health server
- Pass workspace repos metadata through register + task claim responses
- Remove pre-created worktrees from execenv (workdir starts empty)
- Update CLAUDE.md template to instruct agents to use `multica repo checkout`
- Pass MULTICA_DAEMON_PORT, WORKSPACE_ID, AGENT_NAME, TASK_ID env vars to agent
2026-03-29 19:37:48 +08:00