---
title: Project Resources
description: 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](/workspaces). When an [agent](/agents) 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](/skills) 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:

```json
{
  "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**:

```bash
# 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:

```json
{
  "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.
