feat(aircoding): AirCoding V2 baseline — deterministic multi-agent architecture

Forked from OpenCode v1.17.4 with multi-agent system:
- 5 agents: aircoding, scheduler, worker, architect, reviewer
- Deterministic DAG scheduling engine (coordinator_tick)
- Tool whitelists as hard enforcement
- AirCoding validation plugin
- System prompt injection for routing
- V1 requirements: C4 docs, ADR, AGENTS.md, debug-log.md
- Design documents in docs/
This commit is contained in:
airlongdian
2026-06-13 21:41:54 +08:00
commit af3016fe27
5757 changed files with 1170017 additions and 0 deletions

View File

@@ -0,0 +1,139 @@
import { describe, expect, beforeAll, afterAll } from "bun:test"
import { FSUtil } from "@opencode-ai/core/fs-util"
import { Effect, Layer } from "effect"
import { Discovery } from "../../src/skill/discovery"
import { Global } from "@opencode-ai/core/global"
import { Filesystem } from "@/util/filesystem"
import { rm } from "fs/promises"
import path from "path"
import { testEffect } from "../lib/effect"
let CLOUDFLARE_SKILLS_URL: string
let server: ReturnType<typeof Bun.serve>
let downloadCount = 0
const fixturePath = path.join(import.meta.dir, "../fixture/skills")
const cacheDir = path.join(Global.Path.cache, "skills")
const it = testEffect(Layer.mergeAll(Discovery.defaultLayer, FSUtil.defaultLayer))
beforeAll(async () => {
await rm(cacheDir, { recursive: true, force: true })
server = Bun.serve({
port: 0,
async fetch(req) {
const url = new URL(req.url)
// route /.well-known/skills/* to the fixture directory
if (url.pathname.startsWith("/.well-known/skills/")) {
const filePath = url.pathname.replace("/.well-known/skills/", "")
const fullPath = path.join(fixturePath, filePath)
if (await Filesystem.exists(fullPath)) {
if (!fullPath.endsWith("index.json")) {
downloadCount++
}
return new Response(Bun.file(fullPath))
}
}
return new Response("Not Found", { status: 404 })
},
})
CLOUDFLARE_SKILLS_URL = `http://localhost:${server.port}/.well-known/skills/`
})
afterAll(async () => {
void server?.stop()
await rm(cacheDir, { recursive: true, force: true })
})
describe("Discovery.pull", () => {
it.live("downloads skills from cloudflare url", () =>
Effect.gen(function* () {
const fsys = yield* FSUtil.Service
const discovery = yield* Discovery.Service
const dirs = yield* discovery.pull(CLOUDFLARE_SKILLS_URL)
expect(dirs.length).toBeGreaterThan(0)
for (const dir of dirs) {
expect(dir).toStartWith(cacheDir)
const md = path.join(dir, "SKILL.md")
expect(yield* fsys.existsSafe(md)).toBe(true)
}
}),
)
it.live("url without trailing slash works", () =>
Effect.gen(function* () {
const fsys = yield* FSUtil.Service
const discovery = yield* Discovery.Service
const dirs = yield* discovery.pull(CLOUDFLARE_SKILLS_URL.replace(/\/$/, ""))
expect(dirs.length).toBeGreaterThan(0)
for (const dir of dirs) {
const md = path.join(dir, "SKILL.md")
expect(yield* fsys.existsSafe(md)).toBe(true)
}
}),
)
it.live("returns empty array for invalid url", () =>
Effect.gen(function* () {
const discovery = yield* Discovery.Service
const dirs = yield* discovery.pull(`http://localhost:${server.port}/invalid-url/`)
expect(dirs).toEqual([])
}),
)
it.live("returns empty array for non-json response", () =>
Effect.gen(function* () {
// any url not explicitly handled in server returns 404 text "Not Found"
const discovery = yield* Discovery.Service
const dirs = yield* discovery.pull(`http://localhost:${server.port}/some-other-path/`)
expect(dirs).toEqual([])
}),
)
it.live("downloads reference files alongside SKILL.md", () =>
Effect.gen(function* () {
const fsys = yield* FSUtil.Service
const discovery = yield* Discovery.Service
const dirs = yield* discovery.pull(CLOUDFLARE_SKILLS_URL)
// find a skill dir that should have reference files (e.g. agents-sdk)
const agentsSdk = dirs.find((d) => d.endsWith(path.sep + "agents-sdk"))
expect(agentsSdk).toBeDefined()
if (agentsSdk) {
const refs = path.join(agentsSdk, "references")
expect(yield* fsys.existsSafe(path.join(agentsSdk, "SKILL.md"))).toBe(true)
// agents-sdk has reference files per the index
const refDir = yield* Effect.promise(() =>
Array.fromAsync(new Bun.Glob("**/*.md").scan({ cwd: refs, onlyFiles: true })),
)
expect(refDir.length).toBeGreaterThan(0)
}
}),
)
it.live("caches downloaded files on second pull", () =>
Effect.gen(function* () {
// clear dir and downloadCount
yield* Effect.promise(() => rm(cacheDir, { recursive: true, force: true }))
downloadCount = 0
const discovery = yield* Discovery.Service
// first pull to populate cache
const first = yield* discovery.pull(CLOUDFLARE_SKILLS_URL)
expect(first.length).toBeGreaterThan(0)
const firstCount = downloadCount
expect(firstCount).toBeGreaterThan(0)
// second pull should return same results from cache
const second = yield* discovery.pull(CLOUDFLARE_SKILLS_URL)
expect(second.length).toBe(first.length)
expect(second.sort()).toEqual(first.sort())
// second pull should NOT increment download count
expect(downloadCount).toBe(firstCount)
}),
)
})

View File

@@ -0,0 +1,571 @@
import { describe, expect } from "bun:test"
import { Effect, Layer } from "effect"
import { Skill } from "../../src/skill"
import { Discovery } from "../../src/skill/discovery"
import { RuntimeFlags } from "../../src/effect/runtime-flags"
import { EventV2Bridge } from "../../src/event-v2-bridge"
import { Config } from "../../src/config/config"
import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner"
import { FSUtil } from "@opencode-ai/core/fs-util"
import { Global } from "@opencode-ai/core/global"
import { provideInstance, provideTmpdirInstance, testInstanceStoreLayer, tmpdir } from "../fixture/fixture"
import { testEffect } from "../lib/effect"
import path from "path"
import fs from "fs/promises"
const node = CrossSpawnSpawner.defaultLayer
const it = testEffect(Layer.mergeAll(Skill.defaultLayer, node, testInstanceStoreLayer))
const itWithoutClaudeCodeSkills = testEffect(
Layer.mergeAll(
Skill.layer.pipe(
Layer.provide(Discovery.defaultLayer),
Layer.provide(Config.defaultLayer),
Layer.provide(EventV2Bridge.defaultLayer),
Layer.provide(FSUtil.defaultLayer),
Layer.provide(Global.layer),
Layer.provide(RuntimeFlags.layer({ disableClaudeCodeSkills: true })),
),
node,
testInstanceStoreLayer,
),
)
const itWithoutExternalSkills = testEffect(
Layer.mergeAll(
Skill.layer.pipe(
Layer.provide(Discovery.defaultLayer),
Layer.provide(Config.defaultLayer),
Layer.provide(EventV2Bridge.defaultLayer),
Layer.provide(FSUtil.defaultLayer),
Layer.provide(Global.layer),
Layer.provide(RuntimeFlags.layer({ disableExternalSkills: true })),
),
node,
testInstanceStoreLayer,
),
)
async function createGlobalSkill(homeDir: string) {
const skillDir = path.join(homeDir, ".claude", "skills", "global-test-skill")
await fs.mkdir(skillDir, { recursive: true })
await Bun.write(
path.join(skillDir, "SKILL.md"),
`---
name: global-test-skill
description: A global skill from ~/.claude/skills for testing.
---
# Global Test Skill
This skill is loaded from the global home directory.
`,
)
}
const withHome = <A, E, R>(home: string, self: Effect.Effect<A, E, R>) =>
Effect.acquireUseRelease(
Effect.sync(() => {
const prev = process.env.OPENCODE_TEST_HOME
process.env.OPENCODE_TEST_HOME = home
return prev
}),
() => self,
(prev) =>
Effect.sync(() => {
process.env.OPENCODE_TEST_HOME = prev
}),
)
describe("skill", () => {
it.live("discovers skills from .opencode/skill/ directory", () =>
provideTmpdirInstance(
(dir) =>
Effect.gen(function* () {
yield* Effect.promise(() =>
Bun.write(
path.join(dir, ".opencode", "skill", "test-skill", "SKILL.md"),
`---
name: test-skill
description: A test skill for verification.
---
# Test Skill
Instructions here.
`,
),
)
const skill = yield* Skill.Service
const list = (yield* skill.all()).filter((s) => s.location !== "<built-in>")
expect(list.length).toBe(1)
const item = list.find((x) => x.name === "test-skill")
expect(item).toBeDefined()
expect(item!.description).toBe("A test skill for verification.")
expect(item!.location).toContain(path.join("skill", "test-skill", "SKILL.md"))
}),
{ git: true },
),
)
it.live("returns skill directories from Skill.dirs", () =>
provideTmpdirInstance(
(dir) =>
withHome(
dir,
Effect.gen(function* () {
yield* Effect.promise(() =>
Bun.write(
path.join(dir, ".opencode", "skill", "dir-skill", "SKILL.md"),
`---
name: dir-skill
description: Skill for dirs test.
---
# Dir Skill
`,
),
)
const skill = yield* Skill.Service
const dirs = yield* skill.dirs()
expect(dirs).toContain(path.join(dir, ".opencode", "skill", "dir-skill"))
expect(dirs.length).toBe(1)
}),
),
{ git: true },
),
)
it.live("discovers multiple skills from .opencode/skill/ directory", () =>
provideTmpdirInstance(
(dir) =>
Effect.gen(function* () {
yield* Effect.promise(() =>
Promise.all([
Bun.write(
path.join(dir, ".opencode", "skill", "skill-one", "SKILL.md"),
`---
name: skill-one
description: First test skill.
---
# Skill One
`,
),
Bun.write(
path.join(dir, ".opencode", "skill", "skill-two", "SKILL.md"),
`---
name: skill-two
description: Second test skill.
---
# Skill Two
`,
),
]),
)
const skill = yield* Skill.Service
const list = (yield* skill.all()).filter((s) => s.location !== "<built-in>")
expect(list.length).toBe(2)
expect(list.find((x) => x.name === "skill-one")).toBeDefined()
expect(list.find((x) => x.name === "skill-two")).toBeDefined()
}),
{ git: true },
),
)
it.live("skips skills with missing frontmatter", () =>
provideTmpdirInstance(
(dir) =>
Effect.gen(function* () {
yield* Effect.promise(() =>
Bun.write(
path.join(dir, ".opencode", "skill", "no-frontmatter", "SKILL.md"),
`# No Frontmatter
Just some content without YAML frontmatter.
`,
),
)
const skill = yield* Skill.Service
expect((yield* skill.all()).filter((s) => s.location !== "<built-in>")).toEqual([])
}),
{ git: true },
),
)
it.live("discovers skills without descriptions", () =>
provideTmpdirInstance(
(dir) =>
Effect.gen(function* () {
yield* Effect.promise(() =>
Bun.write(
path.join(dir, ".opencode", "skill", "manual-skill", "SKILL.md"),
`---
name: manual-skill
---
# Manual Skill
Instructions here.
`,
),
)
const skill = yield* Skill.Service
const list = (yield* skill.all()).filter((s) => s.location !== "<built-in>")
expect(list.length).toBe(1)
const item = list.find((x) => x.name === "manual-skill")
expect(item).toBeDefined()
expect(item!.description).toBeUndefined()
expect(Skill.fmt(list, { verbose: false })).toBe("No skills are currently available.")
expect(Skill.fmt(list, { verbose: true })).toBe("No skills are currently available.")
}),
{ git: true },
),
)
it.live("discovers skills from .claude/skills/ directory", () =>
provideTmpdirInstance(
(dir) =>
Effect.gen(function* () {
yield* Effect.promise(() =>
Bun.write(
path.join(dir, ".claude", "skills", "claude-skill", "SKILL.md"),
`---
name: claude-skill
description: A skill in the .claude/skills directory.
---
# Claude Skill
`,
),
)
const skill = yield* Skill.Service
const list = (yield* skill.all()).filter((s) => s.location !== "<built-in>")
expect(list.length).toBe(1)
const item = list.find((x) => x.name === "claude-skill")
expect(item).toBeDefined()
expect(item!.location).toContain(path.join(".claude", "skills", "claude-skill", "SKILL.md"))
}),
{ git: true },
),
)
it.live("discovers global skills from ~/.claude/skills/ directory", () =>
Effect.gen(function* () {
const tmp = yield* Effect.acquireRelease(
Effect.promise(() => tmpdir({ git: true })),
(tmp) => Effect.promise(() => tmp[Symbol.asyncDispose]()),
)
yield* withHome(
tmp.path,
Effect.gen(function* () {
yield* Effect.promise(() => createGlobalSkill(tmp.path))
yield* Effect.gen(function* () {
const skill = yield* Skill.Service
const list = (yield* skill.all()).filter((s) => s.location !== "<built-in>")
expect(list.length).toBe(1)
expect(list[0].name).toBe("global-test-skill")
expect(list[0].description).toBe("A global skill from ~/.claude/skills for testing.")
expect(list[0].location).toContain(path.join(".claude", "skills", "global-test-skill", "SKILL.md"))
}).pipe(provideInstance(tmp.path))
}),
)
}),
)
it.live("returns empty array when no skills exist", () =>
provideTmpdirInstance(
() =>
Effect.gen(function* () {
const skill = yield* Skill.Service
expect((yield* skill.all()).filter((s) => s.location !== "<built-in>")).toEqual([])
}),
{ git: true },
),
)
it.live("fails with typed error when requiring a missing skill", () =>
provideTmpdirInstance(
() =>
Effect.gen(function* () {
const skill = yield* Skill.Service
const error = yield* Effect.flip(skill.require("missing-skill"))
expect(error).toBeInstanceOf(Skill.NotFoundError)
expect(error._tag).toBe("Skill.NotFoundError")
expect(error.name).toBe("missing-skill")
expect(error.message).toContain('Skill "missing-skill" not found.')
}),
{ git: true },
),
)
it.effect("exposes tagged expected skill failure classes", () =>
Effect.sync(() => {
const invalid = new Skill.InvalidError({ path: "/tmp/SKILL.md", message: "Invalid skill frontmatter" })
const mismatch = new Skill.NameMismatchError({
path: "/tmp/SKILL.md",
expected: "expected-skill",
actual: "actual-skill",
})
expect(invalid).toBeInstanceOf(Skill.InvalidError)
expect(invalid._tag).toBe("SkillInvalidError")
expect(mismatch).toBeInstanceOf(Skill.NameMismatchError)
expect(mismatch._tag).toBe("SkillNameMismatchError")
}),
)
it.live("discovers skills from .agents/skills/ directory", () =>
provideTmpdirInstance(
(dir) =>
Effect.gen(function* () {
yield* Effect.promise(() =>
Bun.write(
path.join(dir, ".agents", "skills", "agent-skill", "SKILL.md"),
`---
name: agent-skill
description: A skill in the .agents/skills directory.
---
# Agent Skill
`,
),
)
const skill = yield* Skill.Service
const list = (yield* skill.all()).filter((s) => s.location !== "<built-in>")
expect(list.length).toBe(1)
const item = list.find((x) => x.name === "agent-skill")
expect(item).toBeDefined()
expect(item!.location).toContain(path.join(".agents", "skills", "agent-skill", "SKILL.md"))
}),
{ git: true },
),
)
it.live("discovers global skills from ~/.agents/skills/ directory", () =>
Effect.gen(function* () {
const tmp = yield* Effect.acquireRelease(
Effect.promise(() => tmpdir({ git: true })),
(tmp) => Effect.promise(() => tmp[Symbol.asyncDispose]()),
)
yield* withHome(
tmp.path,
Effect.gen(function* () {
const skillDir = path.join(tmp.path, ".agents", "skills", "global-agent-skill")
yield* Effect.promise(() => fs.mkdir(skillDir, { recursive: true }))
yield* Effect.promise(() =>
Bun.write(
path.join(skillDir, "SKILL.md"),
`---
name: global-agent-skill
description: A global skill from ~/.agents/skills for testing.
---
# Global Agent Skill
This skill is loaded from the global home directory.
`,
),
)
yield* Effect.gen(function* () {
const skill = yield* Skill.Service
const list = (yield* skill.all()).filter((s) => s.location !== "<built-in>")
expect(list.length).toBe(1)
expect(list[0].name).toBe("global-agent-skill")
expect(list[0].description).toBe("A global skill from ~/.agents/skills for testing.")
expect(list[0].location).toContain(path.join(".agents", "skills", "global-agent-skill", "SKILL.md"))
}).pipe(provideInstance(tmp.path))
}),
)
}),
)
it.live("discovers skills from both .claude/skills/ and .agents/skills/", () =>
provideTmpdirInstance(
(dir) =>
Effect.gen(function* () {
yield* Effect.promise(() =>
Promise.all([
Bun.write(
path.join(dir, ".claude", "skills", "claude-skill", "SKILL.md"),
`---
name: claude-skill
description: A skill in the .claude/skills directory.
---
# Claude Skill
`,
),
Bun.write(
path.join(dir, ".agents", "skills", "agent-skill", "SKILL.md"),
`---
name: agent-skill
description: A skill in the .agents/skills directory.
---
# Agent Skill
`,
),
]),
)
const skill = yield* Skill.Service
const list = (yield* skill.all()).filter((s) => s.location !== "<built-in>")
expect(list.length).toBe(2)
expect(list.find((x) => x.name === "claude-skill")).toBeDefined()
expect(list.find((x) => x.name === "agent-skill")).toBeDefined()
}),
{ git: true },
),
)
itWithoutClaudeCodeSkills.live("skips Claude Code skills when disabled", () =>
provideTmpdirInstance(
(dir) =>
Effect.gen(function* () {
yield* Effect.promise(() =>
Promise.all([
Bun.write(
path.join(dir, ".claude", "skills", "claude-skill", "SKILL.md"),
`---
name: claude-skill
description: A skill in the .claude/skills directory.
---
# Claude Skill
`,
),
Bun.write(
path.join(dir, ".agents", "skills", "agent-skill", "SKILL.md"),
`---
name: agent-skill
description: A skill in the .agents/skills directory.
---
# Agent Skill
`,
),
]),
)
const skill = yield* Skill.Service
const list = (yield* skill.all()).filter((s) => s.location !== "<built-in>")
expect(list.map((s) => s.name)).toEqual(["agent-skill"])
}),
{ git: true },
),
)
itWithoutExternalSkills.live("skips external skill directories when disabled", () =>
provideTmpdirInstance(
(dir) =>
Effect.gen(function* () {
yield* Effect.promise(() =>
Promise.all([
Bun.write(
path.join(dir, ".claude", "skills", "claude-skill", "SKILL.md"),
`---
name: claude-skill
description: A skill in the .claude/skills directory.
---
# Claude Skill
`,
),
Bun.write(
path.join(dir, ".agents", "skills", "agent-skill", "SKILL.md"),
`---
name: agent-skill
description: A skill in the .agents/skills directory.
---
# Agent Skill
`,
),
Bun.write(
path.join(dir, ".opencode", "skill", "opencode-skill", "SKILL.md"),
`---
name: opencode-skill
description: A skill in the .opencode/skill directory.
---
# OpenCode Skill
`,
),
]),
)
const skill = yield* Skill.Service
const list = (yield* skill.all()).filter((s) => s.location !== "<built-in>")
expect(list.map((s) => s.name)).toEqual(["opencode-skill"])
}),
{ git: true },
),
)
it.live("properly resolves directories that skills live in", () =>
provideTmpdirInstance(
(dir) =>
Effect.gen(function* () {
yield* Effect.promise(() =>
Promise.all([
Bun.write(
path.join(dir, ".claude", "skills", "claude-skill", "SKILL.md"),
`---
name: claude-skill
description: A skill in the .claude/skills directory.
---
# Claude Skill
`,
),
Bun.write(
path.join(dir, ".agents", "skills", "agent-skill", "SKILL.md"),
`---
name: agent-skill
description: A skill in the .agents/skills directory.
---
# Agent Skill
`,
),
Bun.write(
path.join(dir, ".opencode", "skill", "agent-skill", "SKILL.md"),
`---
name: opencode-skill
description: A skill in the .opencode/skill directory.
---
# OpenCode Skill
`,
),
Bun.write(
path.join(dir, ".opencode", "skills", "agent-skill", "SKILL.md"),
`---
name: opencode-skill
description: A skill in the .opencode/skills directory.
---
# OpenCode Skill
`,
),
]),
)
const skill = yield* Skill.Service
expect((yield* skill.dirs()).length).toBe(4)
}),
{ git: true },
),
)
})