pull/16961/head
Dax Raad 2026-03-09 21:36:29 -04:00
parent 656fa191c1
commit e8ee1e239f
2 changed files with 2 additions and 210 deletions

View File

@ -2,4 +2,5 @@ node_modules
package.json
bun.lock
.gitignore
package-lock.json
package-lock.json
plans

View File

@ -1,209 +0,0 @@
# Plan: Project References Feature
Add support for referencing external git repositories in `opencode.json` that can be queried via a dedicated subagent.
## Overview
Users can specify references in config:
```json
{
"references": ["git@github.com:Effect-TS/effect.git", "git@github.com:foo/bar.git#v1.0.0", "/local/path/to/repo"]
}
```
These get cloned/cached to `~/.cache/opencode/references/` and a `reference` subagent can search across them.
---
## Files to Create
### `src/reference/index.ts`
Core reference management module with:
- `parse(url: string): Reference` - Parse git URL or local path, extract branch if present (`#branch` suffix)
- `cachePath(ref: Reference): string` - SHA-256 hash of URL for deterministic directory names
- `isStale(ref: Reference): Promise<boolean>` - Check if `FETCH_HEAD` mtime > 1 hour old
- `fetch(ref: Reference): Promise<void>` - Clone with `--depth 1` or `git fetch`, handle branch checkout
- `ensureFresh(ref: Reference): Promise<void>` - Lazy fetch wrapper (skip if not stale)
- `list(): Promise<Reference[]>` - Get references from config
- `directories(): Promise<string[]>` - Get all cached reference paths for permissions
Types:
```ts
interface Reference {
url: string // Original URL/path
path: string // Cache directory (for git) or resolved path (for local)
branch?: string // Optional branch/tag from URL fragment
type: "git" | "local"
}
```
URL parsing supports:
- `git@github.com:foo/bar.git` → git
- `git@github.com:foo/bar.git#main` → git with branch
- `https://github.com/foo/bar.git#v1.0.0` → git with tag
- `/absolute/path` → local
- `~/relative/path` → local (expanded)
### `src/agent/prompt/reference.txt`
Prompt for reference subagent:
```
You are a multi-project code search specialist. You search across referenced projects
to answer questions about external codebases.
Available references: {references}
Guidelines:
- Search across ALL referenced projects unless the user specifies one
- Report which project(s) contained relevant findings
- Use Glob for file patterns, Grep for content search, Read for specific files
- Return absolute paths so users can locate findings
- Do not modify any files or run destructive commands
```
---
## Files to Modify
### `src/config/config.ts`
Add `references` field to `Config.Info` schema (around line 1174):
```ts
references: z.array(z.string()).optional().describe(
"Git repositories or local paths to reference from subagents"
),
```
### `src/global/index.ts`
Add reference cache path and create directory:
```ts
// Add to Path object:
reference: path.join(cache, "references"),
// Add to mkdir promise array:
fs.mkdir(path.join(cache, "references"), { recursive: true }),
```
### `src/agent/agent.ts`
Add `reference` agent to built-in agents (after `explore` around line 155):
```ts
reference: {
name: "reference",
description: `Search across referenced projects configured in opencode.json under "references". Use this to query code in external repositories.`,
permission: PermissionNext.merge(
defaults,
PermissionNext.fromConfig({
"*": "deny",
grep: "allow",
glob: "allow",
list: "allow",
bash: "allow",
webfetch: "allow",
websearch: "allow",
codesearch: "allow",
read: "allow",
lsp: "allow",
external_directory: {
[Truncate.GLOB]: "allow",
// Reference paths added dynamically via Reference.directories()
},
}),
user,
),
prompt: PROMPT_REFERENCE,
options: {},
mode: "subagent",
native: true,
},
```
Need to inject reference paths into `external_directory` permissions. This requires:
1. Loading references via `Reference.list()`
2. Ensuring they're fresh via `Reference.ensureFresh()`
3. Adding their paths to `external_directory` permissions
**Challenge**: The agent definition is loaded once at startup, but references may change. Solution: Inject reference paths dynamically in the Task tool when creating the subagent session.
### `src/tool/task.ts`
When creating session for `reference` subagent (around line 72):
```ts
// Before Session.create:
let extraPermissions = []
if (params.subagent_type === "reference") {
const refs = await Reference.list()
await Promise.all(refs.map((r) => Reference.ensureFresh(r)))
const paths = refs.map((r) => path.join(r.path, "*"))
extraPermissions = paths.map((p) => ({
permission: "external_directory" as const,
pattern: p,
action: "allow" as const,
}))
}
// Add extraPermissions to the permission array in Session.create
```
Also need to inject available references into the prompt. Modify `SessionPrompt.resolvePromptParts` or add a way to inject context.
### `src/session/prompt.ts` (or similar)
Inject reference list into reference agent's system prompt:
```ts
// When agent.name === "reference", prepend reference info to prompt
const refs = await Reference.list()
const refList = refs.map((r) => `- ${r.url} at ${r.path}`).join("\n")
// Inject into prompt or parts
```
---
## Edge Cases
1. **Clone failures**: Log warning, continue with other references. Don't block subagent.
2. **Network failures**: Use cached version if exists, even if stale. Only error if never cloned.
3. **Branch not found**: Log error, skip that reference.
4. **Local path missing**: Log warning, skip reference.
5. **Invalid URL format**: Log warning, skip reference.
6. **Shallow clone**: Use `--depth 1` for faster clones. For branch-specific URLs, use `--branch <ref> --depth 1`.
---
## Verification
1. Add to `opencode.json`: `{"references": ["git@github.com:effect-ts/effect.git"]}`
2. Invoke `@reference` with query like "show me the Effect schema module"
3. Verify repo cloned to `~/.cache/opencode/references/<hash>/`
4. Verify subagent can search/read files in the cloned repo
5. Wait 1+ hour, invoke again, verify fetch happens
6. Test with local path: `{"references": ["/path/to/local/repo"]}`
7. Test with branch: `{"references": ["git@github.com:foo/bar.git#main"]}`
8. Verify error handling with invalid URL
---
## Files Summary
| File | Action |
| -------------------------------- | ------------------------------------------- |
| `src/reference/index.ts` | Create |
| `src/agent/prompt/reference.txt` | Create |
| `src/config/config.ts` | Add `references` to schema |
| `src/global/index.ts` | Add `reference` path |
| `src/agent/agent.ts` | Add `reference` agent |
| `src/tool/task.ts` | Inject ref permissions + paths into session |
| `src/session/prompt.ts` | Inject reference list into system prompt |