内容
# 思达克尔
**一种高效、规范驱动的跨语言代码库MCP服务器。**
思达克尔为LLM编码代理(Claude Code、Codex CLI等MCP客户端)提供完整的Git原生规范生命周期和跨语言代码智能,使大型重构在平价代理工具上运行,而不是按使用量计费的API:
```
┌─────────────────────────────────────────────────────┐
│ 思达克尔 (MCP) │
结构 │ 跨语言AST映射 (Aider启发) │
│ go:Saxpy ─cgo→ c:launch ─launch→ cu:kernel │
├─────────────────────────────────────────────────────┤
拓扑 │ 级联规范包 (.spectackle/spec.md) │
│ 根 → 模块 → 目录,显式覆盖 │
├─────────────────────────────────────────────────────┤
语义 │ EARS符号,校验 (WHEN …, X SHALL …) │
│ 模糊文本是构建错误 │
├─────────────────────────────────────────────────────┤
生命周期 │ 提案 → 任务 → 完成 → 存档 (+ 可撤销 │
│ 拒绝);日志 = 学习失败的 │
│ 语料库;漂移检测闭环 │
└─────────────────────────────────────────────────────┘
```
## 快速开始
获取预编译的二进制文件 - 无需构建。
通过Homebrew,在macOS和Linux上:
```sh
brew install jxsl13/tap/spectackle
```
或者,从
[最新发布](https://github.com/jxsl13/spectackle/releases/latest)
下载适用于您操作系统的存档;资产命名为`spectackle_<os>_<arch>.tar.gz`(Windows上为`.zip`),适用于
`linux`/`darwin`/`windows` × `amd64`/`arm64`,以及`checksums.txt`。
```sh
curl -L https://github.com/jxsl13/spectackle/releases/latest/download/spectackle_linux_amd64.tar.gz | tar xz
sudo mv spectackle /usr/local/bin/ # 到您的PATH
```
要使用不同的平台,请在URL中替换`<os>`和`<arch>`段:
`spectackle_<os>_<arch>.tar.gz`适用于`<os>` ∈ {`linux`, `darwin`, `windows`} 和
`<arch>` ∈ {`amd64`, `arm64`}。
然后,将**任何**MCP客户端指向二进制文件 - 标准`mcpServers`
入口适用于所有MCP客户端:
```json
{
"mcpServers": {
"spectackle": {
"command": "/绝对路径/to/spectackle",
"args": ["serve"]
}
}
}
```
工作空间根目录自动检测(git根目录,或 `.spectackle/config.yaml`
标记)- 无需其他配置。还没有标记发布?从
源码构建(如下)直到发布第一个版本。
**循环**(由服务器自己的MCP指令教给LLM -
自引导,无需文档):
1. `find scope=rejection` - 学习为什么类似工作以前失败了。
2. `find scope=code` + `get depth=2` - 影响半径跨越Go → C → CUDA/ASM。
3. `draft kind=proposal targets=…` - 单程返回 **上下文包**:影响、绑定EARS合约、类似过去拒绝。
4. 用户审批后:`move to=approved`,`draft`任务,`rule op=add`
合约(服务器编写的EARS,校验门控,自动ID,诱导)。
5. 实现;`check`直到`ok` - 漂移在代码和合约之间通过内容哈希锚点检测,并反向传播为提案。
6. `move to=done` → `to=archived` - 增量合并到活动规范中;
`compact`折叠噪音(拒绝永远不会丢失)。
### 生命周期 - 有限自动机
每个跳转都是可选的:任何正向跳转都是单个移动调用(代币经济);
只有拒绝需要注释,只有存档有守卫(无开放子项)。`blocked`是一个侧状态,既不能
`move`也不能LLM直接设置或清除 - 服务器在轮次限制升级时进入它,其三个出口由
`decide`驱动(参见
[docs/agent-workflow.md](docs/agent-workflow.md) "有界反馈环")。
```mermaid
stateDiagram-v2
direction LR
[*] --> draft: draft工具
draft --> submitted
submitted --> approved
approved --> active
active --> done: check ok
done --> archived: 增量合并到spec.md
done --> active: reopen
draft --> active: 快捷路径(从每个状态跳过合法)
active --> archived: 意味着完成
draft --> rejected
submitted --> rejected
approved --> rejected
active --> rejected
done --> rejected: 需要注释
rejected --> draft: 可撤销
rejected --> active
active --> blocked: 服务器(轮次限制)
done --> blocked: 服务器(轮次限制)
blocked --> draft: decide rescope
blocked --> rejected: decide reject
blocked --> active: decide override-once
archived --> [*]
```
LLM **从不编写规范文件**:所有文件都存放在版本化的`.spectackle/`
文件夹中(每个上下文目录最多三个包文件 - 无文件扩展),由服务器独家编写;
SQLite/FTS5缓存仅限本地,重新构建自磁盘(无迁移,v1之前按设计)。
重点:**Go**,带有任意原生绑定(cgo/C,C++,CUDA,Plan 9 ASM,Objective-C/Metal;Vulkan未来);语言规范解析层使语言成为一个数据值([cookbook](docs/cookbook-new-language.md),29种语言),解析器桥接FFI边界。
## 概念:一个需求引发的动作
以下所有内容都是 `/spectackle <requirement>`
([.claude/commands/spectackle.md](.claude/commands/spectackle.md),相同生成内容也存在于[AGENTS.md](AGENTS.md))字面执行时,用户向编排器提供需求时运行的:八个工具驱动的步骤,每个步骤都有具体的磁盘或集群效果。
```mermaid
flowchart TD
Start(["/spectackle <requirement>"]) --> R1
R1["1 研究<br/>research q=<requirement><br/>只读:影响包 + 绑定合约 + 过去拒绝"] --> RQ{"包回答它?"}
RQ -->|"是"| D2
RQ -->|"否"| RI["draft kind=research<br/>R-nnnn, 详尽简报<br/>委托给新的子代理"] --> D2
D2["2 起草<br/>draft kind=proposal targets=<ids><br/>写入work.md:P-nnnn 状态=draft<br/>日志:创建"] --> G3
G3["3 审查<br/>grill id=P-nnnn<br/>批评包;未锚定目标获取<br/>规则操作=添加,写入spec.md + anchors.tsv<br/>头部戳记:日期"] --> DEC4
DEC4{"4 决定,如果仍然不确定<br/>决定操作=询问"}
DEC4 -->|"包已经回答"| AP5
DEC4 -->|"需要用户"| ADRN["ADR-item<br/>work.md + 日志"]
ADRN -->|"决定操作=答案<br/>可能在后续会话中"| AP5
AP5["5 批准<br/>移动到=approved<br/>+ 起草类型=任务 x N, 详尽主体<br/>写入T-nnnn到work.md"] --> FO6
FO6["6 分发<br/>按不相交范围划分已批准的任务"] --> IMPL
subgraph IMPL["每个任务一个新鲜的实现者"]
direction LR
I1["实现者A<br/>租赁声明范围A<br/>git工作树 .spectackle/wt/T-1<br/>移动到=active"]
I2["实现者B<br/>租赁声明范围B<br/>git工作树 .spectackle/wt/T-2<br/>移动到=active"]
end
IMPL --> SUB["移动到=done<br/>工作操作=提交:门控,提交代码,<br/>合并主线, 重放 .spectackle 增量"]
SUB --> CK7
CK7["7 检查<br/>分类anchors.tsv"] -->|"干净"| AR8
CK7 -.->|"门控失败,漂移,或轮次耗尽"| SIDE(["项目状态机处理这个,<br/>重新打开 / 阻止 / 拒绝,见下文"])
AR8["8 存档<br/>移动到=archived,意味着完成"] --> SPEC[("spec.md<br/>## 意图 += 增量")]
CK7 -.-> ANCH[("anchors.tsv<br/>重新加戳")]
ANCH -.->|"漂移发现馈送<br/>下一轮的研究"| NEXT(["回到1 研究"])
```
每个提案/任务框项也通过其自己的状态机移动 - 第二个轴工作流驱动但未显示。它
被分解为自己的图表,而不是折叠到上面的图表中,因为组合两者会产生一张图片,没人能一眼看懂;右边的注释将每个状态与达到它的步骤编号联系起来:
```mermaid
stateDiagram-v2
direction LR
[*] --> draft: 2 起草
draft --> submitted
submitted --> approved: 5 批准
approved --> active: 6 分发
active --> done: 7 检查ok
done --> archived: 8 存档
done --> active: reopen(轮次++)
active --> blocked: 轮次 == max_rounds
done --> blocked: 轮次 == max_rounds
blocked --> draft: decide rescope
blocked --> active: decide override-once
blocked --> rejected: decide reject
draft --> rejected: 需要注释
submitted --> rejected: 需要注释
approved --> rejected: 需要注释
active --> rejected: 需要注释
done --> rejected: 需要注释
rejected --> draft: 可撤销
rejected --> active
archived --> [*]
note right of draft: 步骤2在此处铸造项目
note right of approved: 步骤5,用户审批
note right of active: 步骤6,实现者在其工作树中编辑
note right of done: 步骤7,检查门控
note right of archived: 步骤8,增量合并到spec.md
note right of blocked: 服务器专用侧状态,移动永远不能设置或清除它
```
**为什么规则生活在它们所管辖的代码旁边。** 一个`spec.md`可以存在于
任何目录的`.spectackle/`文件夹中,其规则适用于该目录及其以下的所有目录,除非更深的文件显式覆盖规则ID或切断继承(`inherits: false`)。这与`.gitignore`和`CLAUDE.md`如何级联类似,而且这并不仅仅是外观:解析路径上的规则只会遍历路径所处的目录树 -
影响半径为三个文件的文件拉取最多三个目录树,从不整个语料库。
**为什么使用EARS而不是自由文本。** 每个规则句子必须解析为六个固定模式之一(`WHEN … SHALL`,`IF … THEN … SHALL`,…);像“服务器应该适当地处理错误”这样的模糊文本是一个校验 **错误**,不是样式问题。回报:一个规则是一个单一的条件,可衡量的子句,LLM可以将其确定性地翻译成影响范围内的任何语言,因为没有解释偏差导致翻译错误。执行发生在写入 **之前** - `rule`工具在持久化之前编写和校验句子 - 所以一个能够写入磁盘的规范包已经通过校验器检查了语法。
**为什么规则的代码位置和措辞被跟踪为两个单独的哈希值。** 一个锚点将一个规则绑定到一个代码范围,并记录两个独立的内容哈希值:一个在规范代码范围上,一个在规则自己的句子上。它们在独立的轴上漂移 - 代码可以被重构而合约的含义不变,而合约的措辞可以被收紧而绑定的代码不会移动一行 - 所以交叉它们给出一个分类,一个“更改”位无法做到:未更改的代码和规则是`ok`(或`moved`,如果仅位置移动 - 静默刷新,不是漂移);代码更改但措辞未更改是`evolved`,服务器自动修复的类别,因为合约仍然描述代码正确,只有存储的哈希值过时;任何措辞更改(`tightened`,或`diverged`如果代码也更改)永远不会自动修复,因为作者的意图可能已更改,需要人类查看。
**为什么任何东西都不会被丢弃。** 拒绝一个项目会将其完整的内容快照到追加日志中,然后从活动`work.md`中删除;压缩可能会折叠普通的`create`/`move`/`rule`/`drift`噪音,但`reject`、`archive`和`compact`行始终保持原样。那就是为什么拒绝可以稍后撤销 - 服务器从自己的快照中重建项目 - 以及为什么拒绝语料库值得搜索在重复失败方法之前:语料库可以悄悄丢失条目时不值得信赖。
**为什么强大的编排器起草,而廉价的实现者只执行。** 探索,而不是编辑,是代理编码的昂贵部分 - 必须grep树并从头开始重建约束的模型比更改本身花费的代币更多。思达克尔将此成本放在一个持久的、复杂的模型编排器上,起草、审查,从不重复两次,并将每个廉价的实现者交给一个任务主体,没有什么需要发现。因为简报吸收了探索,代币成本随着批准的任务数量扩展,而不是代码库的大小;因为作用域租赁 - 而不是约定 - 证明两个任务的声明路径是不相交的,因此可以并行运行任意数量的实现者在其工作树中,从不合法地触摸同一个文件。
**为什么LLM永远不会写入这些文件。** 本节和下一节描述的每个文件都是服务器独家编写的; LLM永远只调用一个工具。那购买了三个手编辑无法实现的东西:EARS校验门控在持久化之前运行;写入的副作用(日志事件,锚点戳记,搜索索引更新)与写入本身同时发生,而不是作为单独的步骤,某人可以忘记;并且每个服务器编写的文件都带有模式戳记,因此格式更改是硬显式中断而不是静默发散。
## 链条,实时
实时输出:
```
n go:saxpy.Saxpy 函数 examples/saxpy/saxpy/saxpy.go:21-36 sig=(n int,a float32,x,y []float32)error
n c:launch_saxpy 函数 examples/saxpy/saxpy/kernels/saxpy.cu:14-39
n go:main.main~2 函数 examples/saxpy/main.go:12-24 sig=()
n cu:saxpy_kernel 内核 examples/saxpy/saxpy/kernels/saxpy.cu:7-12
e go:saxpy.Saxpy cgo c:launch_saxpy via=examples/saxpy/saxpy/saxpy.go:25
e go:main.main~2 调用 go:saxpy.Saxpy via=examples/saxpy/main.go:20
e c:launch_saxpy 启动 cu:saxpy_kernel via=examples/saxpy/saxpy/kernels/saxpy.cu:27
r SXP-API-002 E examples/saxpy WHEN Saxpy 被调用时n小于1或切片长度小于n,Go绑定应当返回一个非nil `error`边界。
r SXP-API-001 N examples/saxpy IF CUDA包装器返回一个非零状态,THEN Go绑定应当将状态包装在一个包含数字CUDA状态代码的错误中。
r-root SPX-ARC-001 SPX-ARC-002 SPX-ARC-003 SPX-ARC-004 SPX-ARC-005 SPX-REPO-001 SPX-REPO-002 SPX-TST-001 SPX-SWM-001 SPX-SWM-002 SPX-SWM-003 SPX-SWM-004 SPX-SWM-005 SPX-SWM-001 SPX-SWM-002 SPX-SWM-003 SPX-SWM-004 SPX-SWM-005 SPX-SWM-006 SPX-SWM-007 SPX-ARC-006
```
这是仓库中提供的二进制文件运行`get {"id":"go:saxpy.Saxpy","depth":2}`时的实际输出 - 不是模拟的:`n`范围,`e`边,节点绑定合约(`r`/`r-root`)都来自一次调用。
## 从源码构建
```sh
make build # -> bin/spectackle (纯Go,CGO_ENABLED=0)
./bin/spectackle lint . # 校验所有EARS规范包
```
在Claude Code(或其他MCP客户端)中注册:
```json
{
"mcpServers": {
"spectackle": {
"command": "/绝对路径/to/bin/spectackle",
"args": ["serve"]
}
}
}
```
工作空间根目录自动检测(`.spectackle/config.yaml`标记,然后是git根目录,然后是`-root`)。
## CI/CD
两个GitHub Actions工作流,都镜像了本地`make`所做的所有事情 -
因此在您的机器上绿色`make all`与CI运行的门控相同。
**CI** (`.github/workflows/ci.yml`,在每次推送到`main`和每个拉取请求上):`make build`,`go vet ./...`,`go test -race ./...`,覆盖率门控(`make cover`,≥70%),EARS校验器模糊运行(`make fuzz`),`spectackle lint .`检查每个规范包,以及`make smoke`。
最后一个步骤是自我托管门控,也是值得了解的:CI在标准输入/输出上启动新构建的服务器,并在此仓库上调用`check`工具,要求输出完全为`ok`。漂移发现,未锚定合约或未存档的已完成生命周期项目都会导致构建失败 - 仓库的自身记录也是CI验证的内容,而不仅仅是代码。
**发布** (`.github/workflows/release.yml`,在任何`v*`标签推送时):运行`go test -race ./...`作为门控,然后goreleaser构建并发布amd64和arm64上的linux,darwin和windows的CGO-free二进制文件,带有存档,`checksums.txt`和生成的更改日志。版本由构建时从标签中提取的版本号报告为`spectackle version`。macOS和Linux的Homebrew公式在同一运行中推送到`jxsl13/homebrew-tap`;不需要代码签名证书。
截断发布是一个命令 - 参见
[docs/release.md](docs/release.md),包括`make release-snapshot`进行本地试运行,不会发布任何内容。
### 入口点
- **`/spectackle`**(Claude Code仓库命令,`.claude/commands/spectackle.md`)
- 一种双模式命令:给定一个需求(`/spectackle add rate limiting to the API`),它驱动完整的SDD生命周期端到端(研究 → 草稿 → 审查 → 不确定时决定 → 批准 → 分发给新鲜的实现者 → 检查 → 存档)。
- **`/spectackle-state`** - 显式仅状态别名,无生命周期副作用。
- **`/mcp__spectackle__workflow`** / **`/mcp__spectackle__next`** /
**`/mcp__spectackle__state`** - 与原生MCP提示相同的入口点,可在任何支持MCP的客户端中使用;服务器注册后,`workflow`接受与两模式`/spectackle`命令相同的可选`task`参数,因此两模式入口点在Claude Code外部也完全相同。
## 无头快速入门(从编码代理/CI驱动服务端)
没有MCP客户端?二进制文件自带了一个MCP客户端(CLI-002)—— 不需要外部包装脚本,也不需要手动编写JSON-RPC框架,以避免出错。所有操作都通过`call`子命令进行:
```sh
spectackle call [-root 目录] [-http 地址] [-instructions] [名称 [JSON]]
```
**一次性调用** —— `名称`加上一个可选的JSON参数对象,只发出一次调用,生成一个新的服务端,通过标准输入输出运行,并立即关闭:
```sh
./bin/spectackle call -root . find '{"q": "saxpy", "scope": "code"}'
```
只有工具的渲染文本输出到标准输出,字节相同,一个尾部换行符 —— 没有信封,没有前缀,没有横幅,因此它可以与管道中的`grep`/`jq`/其他命令组合使用。连接诊断和任何生成的服务端stderr输出到stderr。 拒绝(`! GATE …`,架构验证错误,…)仍然将其文本输出到标准输出,但退出状态为非零 —— 根据退出码而不是文本进行脚本编写。
**多调用批次** —— 省略`名称`并在标准输入上输入一个`{"name": ..., "arguments": ...}` JSON对象,每个调用在批次中运行在一个**单个会话**中,因此工作区索引一次用于整个批次,而不是每个调用一次:
```sh
./bin/spectackle call -root . <<'JSON'
{"name": "swarm", "arguments": {}}
{"name": "find", "arguments": {"q": "saxpy", "scope": "code"}}
JSON
```
**指令清单** —— `-instructions`打印服务端的`initialize`握手指令(工作流合约:生命周期循环,集群协议,编排/扇出,模型分层)并退出。将其原样提供给驱动LLM;静默丢弃它是一个错误,就像本项目中早期的手动包装脚本一样。
```sh
./bin/spectackle call -root . -instructions
```
**命名代理**:在环境中设置`SPECTACKLE_AGENT=<name>`,以便群集身份(租约,心跳,学习)稳定在否则短暂的`call`调用中 —— 协调状态存储在共享的`.spectackle/cache/coord.db`中,而不是进程中。
第一个调用的会话:`swarm {}`(其他人正在工作,新学习),然后`get {"id": "."}`(根规则 + 活动项)—— 上面的指令清单教导了循环的其余部分。
## 常驻服务(推荐用于多个调用)
为每个`call`生成一个全新的服务端适合于单个一次性调用,但其成本是**每个调用都从头开始索引工作区** —— 这个仓库有163个文件,每次启动时都会这样。 对于实施者群、长时间运行的CI作业或跨多个`call`调用发出多个调用的任何操作来说,这是浪费的,因为常驻进程索引**一次**并回答后续调用来自热缓存。
**推荐:`make dev`**。它构建当前树,停止已经运行的开发服务端,启动一个通过Streamable HTTP与pid文件的新服务端,并且仅在实际的`state`调用回答后返回 —— 因此可以安全地重复运行(例如每次合并后;参见[CONTRIBUTING.md](CONTRIBUTING.md#the-resident-server-must-be-rebuilt-and-restarted-after-every-merge)),而不会在单个端口上留下两个服务端或卡在过时的pid文件上:
```sh
make dev # 构建 + 重启,直到它回答一个真正的调用
make dev-status # 报告它是否正在运行,而不更改任何内容
make dev-stop # 停止它(如果没有运行,则为no-op)
```
它默认绑定`127.0.0.1:7412`,并带有pid文件`bin/dev.pid` —— 可以使用`DEV_ADDR=... DEV_PIDFILE=...`覆盖,与Makefile中的`BIN`/`GO`/etc相同。
**手动调用**,适用于不使用`make`的人 —— 将其绑定到localhost并带有pid文件,以便它有一个可停止的句柄:
```sh
./bin/spectackle serve -root . -http 127.0.0.1:7331 -pidfile .spectackle/serve.pid &
```
使用`-http`指向它,而不是生成一个新的标准输入/输出服务端 —— 相同的单次调用和标准输入批次输入模式,相同的字节相同的标准输出渲染,只有传输发生变化:
```sh
./bin/spectackle call -http 127.0.0.1:7331 find '{"q": "saxpy", "scope": "code"}'
```
任何其他Streamable-HTTP功能的MCP客户端也可以指向`http://127.0.0.1:7331`。 使用服务端写入的pid文件停止常驻服务端:
```sh
kill "$(cat .spectackle/serve.pid)"
```
`-pidfile PATH`是可选的(默认为空,不写入文件),并且不依赖于`-http` —— 它帮助后台标准输入/输出服务端一样。
## 编排的群集工作流(廉价的新子代理)
此仓库的开发方式是其预期用途:**一个强大的编排器 + 一个廉价的模型上的新鲜、最小上下文的实现者代理**。两个角色永远不会重叠:
- **编排器**(复杂/强大模型,持久上下文)—— 起草提案,编写详尽的任务正文,审查实现者输出,运行最终的门控/验证,合并,并是*唯一*提交或打开PR的角色。
- **实现者**(更简单/廉价的模型,零先前上下文)—— 拉取*一个*批准的任务,不进行任何探索,实现它,测试它,并将其退回。
每个实现者都是全新的,仅有两个输入:(a)任务的完整简介(`get <T-id>` —— 确切的文件,API,命令,约束,已经由编排器解决)和(b)上面的无头驱动程序配方。 实现者循环是固定的和机械的:
```
1. 租约声明 — 声明任务的声明范围(路径);在冲突时,选择不同的任务,永远不会空闲等待。
2. 移动活动 — 将任务ID移动到“活动”。
3. 实现 — 仅编辑声明的范围;运行声明的构建/测试/验证命令,直到绿色。
4. 移动完成 — 将任务ID移动到“完成”。
5. 释放租约 — 立即释放已声明的范围。
```
为什么是这种形式:**探索是代理编码中最昂贵的部分**,不是编辑 —— 所以服务端为编排器替换它(打包`find`/`get`/上下文而不是grep树),任务正文为实现者替换它(无需发现,只有执行)。 由于简介由复杂模型编写,因此简单模型永远不会探索;这保持了复杂模型的上下文不受实现噪声的影响,并且它使令牌成本随任务数量而不是代码库大小而缩放。**分离**在并发运行的实现者中通过范围租约强制执行,而不是通过约定 —— 两个代理永远不能合法地同时触摸相同的路径。 共享的`.spectackle/cache/coord.db`(租约,学习,拒绝,日志)是群集的共同大脑:每个兄弟姐妹通过`swarm`实时看到声明和拒绝,因此失败的方法永远不会被盲目重试。
**扇出**:一旦一批任务被批准,编排器将它们按不相交范围分区(租约证明不相交),为每个任务并行生成一个新鲜的实现者,并仅序列化共享文件本身的连接。
对于长时间运行的群集,请将服务端作为**常驻服务**运行(参见上面的“常驻服务”和[docs/architecture.md](docs/architecture.md) §8),而不是为每个代理生成一个新的标准输入/输出进程 —— 一个共享的进程,一个共享的缓存,没有每个实现者的冷启动。
完整的角色分解,时序图和“详尽任务正文”检查清单:[docs/agent-workflow.md](docs/agent-workflow.md)。
## 持久化数据结构
Spectackle在磁盘上或共享数据库中保留的每个结构,它存在的位置,它包含的内容,以及 - 实际上解释布局的部分 - 为什么它是一个单独的结构,而不是一个附加到其他结构上的字段。
**.spectackle/spec.md** —— 活动的规格,每个需要目录范围规则的目录都有一个(根目录的是仓库范围的)。 YAML前置内容(`schema`,`prefix`,`scope`,`inherits`,`overrides`)加上markdown正文:一个`## intent`(和可选的`notes`/`design`/`context`)散文部分,和一个`## <RULE-ID>`标题,每个都有一个可选的`{applies: id,id}`绑定和一个可选的`Rationale:`段落。 规则从根 → 祖先脊柱 → 最近的目录解析,默认情况下更深的文件扩展, 仅在显式`overrides:`或`inherits: false`时获胜。 它是自己的文件,每个目录都有自己的文件,因为它是唯一一个用于git审查的结构 —— 并且因为目录范围的级联(参见上面的概念)只有在目录可以有自己的文件和自己的作用域时才是可能的; 将每个目录的规则折叠成一个仓库范围的文档将崩溃整个“仅加载影响半径的脊柱”论点。
**.spectackle/work.md** —— **仅活动项**:状态为draft/submitted/approved/active/done/blocked的每个项都有一个`## <ID> <title>`块,带有平面`key: value`机器头部(类型,状态,创建时间,父项,引用,目标,规则,目标,轮次,烧烤,需要,覆盖和ADR字段context/decision/consequences/status)和自由文本正文。 一旦某个项被拒绝或存档,其块就会从work.md中删除。 仅活动是因为项的结果是一个历史事实,没有人再编辑,所以将其保存在work.md中会无限增长仓库的大小 —— 而且每个工具调用都读取work.md,因此一个永远不会停止增长的文件会使每个调用因不再需要的信息而变慢。 没有块被删除时丢失任何信息;日志已经包含完整的历史记录,因为`archive`/`reject`事件永远不会被压缩掉。
**.spectackle/journal.ndjson** —— 附加日志,一个`.spectackle/`文件夹一个,每行一个紧凑的JSON对象(`create`/`move`/`rule`/`archive`/`reject`/`drift`/`compact`/`start`/`submit`/`abort`/`grill`/`decide`/`escalate`),每个都有一个唯一的事件ID和写入代理。 它是work.md和spec.md当前快照的基础。 压缩可能会折叠普通的`create`/`move`/`rule`/`drift`噪音,但`reject`,`archive`和`compact`行始终保留原样 —— 附加日志,没有一个例外,因为拒绝快照必须在压缩后存活下来,以便`move`能够撤销它,并且可搜索的拒绝语料库(`find scope=rejection`)只有在条目不会被静默删除时才是可信的。 服务端写入的`.gitattributes`专门为此文件设置`merge=union` —— 附加内容在分支间无冲突合并,因此高换行率的文件被设计为永远不需要真正的合并。
**.spectackle/anchors.tsv** —— 仅限根目录,制表符分隔的行`rule node file span chash rhash`,将EARS规则绑定到它所对应的代码范围。 `chash`哈希规范的代码范围(CRLF→LF,删除尾部空格,保留缩进 —— 它在Plan 9 asm等语言中是语义的);`rhash`哈希规则的句子。 它们是两个哈希,而不是一个,因为代码和规则的措辞在独立的轴上发生变化:代码可以被重构而不会改变合同的含义,而合同的措辞可以被收紧而不会移动代码行。 交叉两个轴是分类检查报告的内容:相同的代码,相同的规则→`ok`(或`moved`如果仅位置偏移 —— 静默刷新,不漂移);代码更改,规则未更改→`evolved`;代码未更改,规则更改→`tightened`;两者都更改→`diverged`。 只有`evolved`是自动修复的 —— 规则仍然正确地描述了代码,只有存储的哈希已过时 —— 因为`tightened`和`diverged`都涉及措辞更改,这意味着作者的意图可能已更改,而服务端不应静默掩盖实际的合同更改。
**.spectackle/config.yaml** —— 根目录设置:模式,`langs`,`ignore`/`ignore_regex`修剪模式,`budget_default`,压缩阈值(`journal_max`,`done_max`),群集调优(`lease_ttl`,`agent_ttl`),反馈调优(`max_rounds`,`grill`),`worktrees_dir`,`verify`门控命令。 这是唯一一个人类预期手动编辑的结构:服务端在第一次使用时使用所有默认值写入并注释它,因此该文件兼作自己的参考文档, 而且 - 与列表中的其他结构不同,*现有*的config.yaml永远不会被服务端重新生成或重写;手动编辑是永久的,直到用户更改它们。
**.spectackle/cache/** —— 根目录,gitignored,持有`index.db`(SQLite FTS5,纯Go,搜索索引每个规则/项/日志/拒绝记录,以及索引运行时跨语言代码图)和`coord.db`(如下)。 `cache/`下的所有内容都源自版本文件(spec.md/work.md/journal.ndjson/anchors.tsv)加上源代码树的扫描 —— 这里存储的内容没有任何不是从这些来源重建的内容。 该不变式使得删除目录是安全的:一个不匹配的生成戳已经触发相同的drop-and-rebuild自动发生,因此手动删除它只是达到服务端将要达到的结果的一个较慢路径,从不会导致知识丢失。
**`coord.db`** —— `.spectackle/cache/coord.db`的*main*仓库(链接的工作树通过`git rev-parse --git-common-dir`解析); WAL模式的SQLite。 拥有代理注册表(心跳),范围租约(路径/项目对,有过期时间,因此冲突的声明可以命名其持有者),全局项目/规则ID计数器(floor-seeded,因此删除DB永远不会使ID回归或让两个工作树铸造相同的ID),群集事件日志兄弟姐妹搭载学习内容,工作树记录和单个集成锁。 它是SQLite/WAL而不是普通文件,因为多个spectackle进程 - 每个代理会话一个 - 同时写入它:普通文件在并发读写时是竞争的,而WAL模式的SQLite给了每个写入者一个真正的事务,因此声明或来自一个进程的计数器增量永远不会被另一个进程的并发写入丢失。
**知识工件** —— 由`internal/knowledge`生成,这是**可移植交换,不是工作区状态**:默认情况下不在工作区的`.spectackle/`文件夹中,而是独立文档,旨在被调用者定向到任何地方(审查或将种子到另一个存储库)。 与spec.md相同的markdown家族前置内容(`schema`,`kind: knowledge`,`sources`),每个`## rule|adr|intent <key>`部分。 包含规则句子,ADR和意图正文,直接从一个或多个存储库的spec/item语料库中提取 - 从不改述 - 每个都带有重复计数和断言它的源存储库+目录(或泛化它)。 将其与spec.md混淆会使一个固有的跨存储库工件看起来像一个本地合约,而它在结构上不是。
**两个解释上述结构形状的不变式:
- **永远没有迁移,永远不会在1.0之前**。 服务端写入的每个文件都带有`schema`戳(今天的`v0`);一旦磁盘格式更改,戳也会更改,并且带有旧戳的文件是一个硬工具错误(“regenerate”),永远不会被服务端尝试原地升级。 在1.0之前,格式可能会随时更改 - 代码库中没有迁移机制来破坏。
- **LLM永远不会直接写入这些文件中的任何一个**。 铸造一个项目,编写一个规则,附加日志事件,标记锚点,递增计数器 - 所有的写入路径都是通过工具调用达到的服务端路径。 这使得其他所有属性都成立:lint gate在写入之前运行,而不是之后;写入的副作用(日志事件,锚点戳,索引更新)与它一起原子发生,并且`spectackle lint .`在CI中可以信任磁盘上的任何内容都经过了产生它的单个门控。
## 文档
- [docs/lifecycle.md](docs/lifecycle.md) — **生命周期架构**:存储、搜索、状态机、压缩、漂移/反向传播
- [docs/agent-workflow.md](docs/agent-workflow.md) — 编排器 + 新实现者群集工作流
- [docs/tools.md](docs/tools.md) — 15 个 MCP 工具:JSON 模式 + 输出语法
- [docs/spec-cascade.md](docs/spec-cascade.md) — 级联规范包:格式、解析、创作
- [docs/ears.md](docs/ears.md) — EARS 语法和检查器代码
- [docs/architecture.md](docs/architecture.md) — 跨语言 AST 分析(解析器、解析器、图)
- [docs/cookbook-new-language.md](docs/cookbook-new-language.md) — 添加语言:一个规范值 + 两个单行注册
- [docs/example-go-cuda.md](docs/example-go-cuda.md) — Go → CUDA 生命周期转录
- [docs/release.md](docs/release.md) — 剪切版本:标签、门控、制品
- [docs/roadmap.md](docs/roadmap.md) — 里程碑和仍未完成的内容
## 自测试
该存储库在 `.spectackle/` 包中携带自己的合约;
如果任何提交的规范违反 EARS 语法,则 `go test ./...` 将失败,并且
`check` 工具必须在存储库本身上返回干净的结果 — spectackle 是其
自己的第一个用户。
MCP 配置
以下是此 MCP Server 的配置信息,可直接复制到 Cursor 或其他 MCP 客户端中使用。
mcp.json
连接信息
猜你喜欢
暂无推荐服务器