Multica Docs

Project Resources

Attach typed pointers (Git repos today, more later) to a project so agents can pick them up as scoped context.

A Project Resource is a typed pointer — a Git repo URL today, a Notion page or document link tomorrow — attached to a project. When an agent runs against an issue inside that project, the daemon automatically writes the project's resource list into the agent's working directory and into its meta-skill prompt.

The result: the agent knows which repo to check out, which docs are the "primary references" for this project, without anyone copy-pasting context into the issue body.

Mental model

A project is no longer just a label. It is a small resource container:

  • A project has 0..N resources.
  • A resource has a resource_type (e.g. github_repo) and a resource_ref (a JSON payload typed by resource_type).
  • New resource types add a string + a handler. No schema migration. No frontend rewrite.

This shape is intentional — it's the same pattern Multica already uses for agent providers: a type discriminator and a typed payload. It keeps the schema stable so adding "Notion page", "Google Doc", "uploaded file", or "external URL" later is a small, additive change.

Today: github_repo

The first resource type ships ready to use:

{
  "resource_type": "github_repo",
  "resource_ref": {
    "url": "https://github.com/owner/repo",
    "default_branch_hint": "main"
  }
}

default_branch_hint is optional — if present, the daemon surfaces it in the meta-skill so the agent knows which branch to base its work on.

Attaching repos at project creation

In the Web or Desktop app, opening New project now shows a Repos pill alongside Status / Priority / Lead. Selecting workspace-bound repos (or pasting an ad-hoc URL) attaches them as github_repo resources the moment the project is created.

From the CLI:

# Create + attach in one shot. The server attaches resources in the same
# transaction as the project create — invalid resources roll back the whole
# operation, so you never end up with a project that has half its resources.
multica project create \
  --title "Agent UX 2026" \
  --repo https://github.com/multica-ai/multica

# Manage resources later
multica project resource list <project-id>
multica project resource add  <project-id> --type github_repo --url <url>
multica project resource remove <project-id> <resource-id>

# Generic escape hatch for any resource_type the server understands —
# no CLI change needed when a new type ships:
multica project resource add <project-id> \
  --type notion_page \
  --ref '{"page_id":"…","title":"…"}'

--repo may be repeated; each value is attached as a separate github_repo resource.

What the agent sees at runtime

When the daemon spawns an agent for an issue inside a project, two things happen:

1. .multica/project/resources.json

A structured pass-through of the API response, written into the agent's working directory:

{
  "project_id": "…",
  "project_title": "Agent UX 2026",
  "resources": [
    {
      "id": "…",
      "resource_type": "github_repo",
      "resource_ref": {
        "url": "https://github.com/multica-ai/multica",
        "default_branch_hint": "main"
      }
    }
  ]
}

Skills, helper scripts, or the agent itself can parse this file when they need the exact set of resources for the run.

2. A "Project Context" section in the meta-skill prompt

The agent's CLAUDE.md / AGENTS.md (depending on provider) now includes a human-readable summary:

## Project Context

This issue belongs to **Agent UX 2026**.

Project resources (also written to `.multica/project/resources.json`):

- **GitHub repo**: https://github.com/multica-ai/multica (default branch: `main`)

Resources are pointers — open them only when relevant to the task. For
`github_repo` resources, use `multica repo checkout <url>` to fetch the code.

The text is intentionally minimal. The full payload is on disk; the prompt only orients the agent so it knows the project exists and what's attached.

Failure mode

Resource fetch is best-effort. If the API call fails, the project section is omitted from the prompt and the file is not written, but the task still starts. Agents never block on missing project context.

Adding a new resource type

The whole point of the abstraction is that new types are cheap. The full path:

  1. Server validator (server/internal/handler/project_resource.go) — add a case in validateAndNormalizeResourceRef that parses and normalizes the new payload.
  2. Daemon meta-skill formatter (server/internal/daemon/execenv/runtime_config.go) — add a case in formatProjectResource so the agent prompt renders the new type as a readable bullet.
  3. TypeScript types (packages/core/types/project.ts) — extend ProjectResourceType and add the payload interface.
  4. UI renderer (packages/views/projects/components/project-resources-section.tsx) — add a case in ResourceRow for the new type.

There is no schema migration, no new sqlc query, no new endpoint, and no CLI change — the CLI's generic --ref '<json>' flag accepts any payload the validator understands, so day-one support for a new type is purely the four steps above. (You may optionally add a per-type CLI shortcut later; not required.)

The same project_resource table and the same three CRUD calls handle every type.

Workspace repos vs. project repos

The repo list shown to the agent (## Repositories block in CLAUDE.md / AGENTS.md) is chosen by the daemon claim handler with this precedence:

  • Project has at least one github_repo resource → only those repos are surfaced to the agent. Workspace-bound repos are intentionally hidden so the agent doesn't have to guess which one belongs to this issue.
  • Project has no github_repo resources (or the issue isn't in a project) → fall back to the workspace's repo list as before.

This keeps the agent's working set tight: when a project is explicit about its repos, that's the authoritative answer. The structured resource list at .multica/project/resources.json always carries the full set, so a skill that wants to inspect everything still can.

The daemon mirrors this on the checkout side: when a task arrives with project-scoped github_repo URLs, those URLs are merged into the per-workspace allowlist and synced into the local repo cache before the agent spawns. So a project repo URL that isn't bound at the workspace level is still a valid argument to multica repo checkout — the daemon won't reject it as "not configured." The allowlist split is internal: workspace-bound URLs and task-scoped URLs are tracked separately, so a workspace-repos refresh doesn't accidentally revoke a project URL mid-run.

What's intentionally not in scope here

  • Cross-project sharing. Each resource lives on exactly one project today.
  • Per-skill resource scoping. All resources are visible to every skill on the agent's run; type-aware filtering is a follow-up.
  • Caching / sync. github_repo is just metadata — checkout still happens via multica repo checkout on demand. Cached document text for Notion / Google Docs will arrive with those types.

These are deliberate omissions — the goal of the first cut is to validate the abstraction with the smallest set of moving parts.