@namewta/speculo 0.7.5 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/README.md +4 -5
  2. package/dist/src/cli.js +18 -13
  3. package/dist/src/cli.js.map +1 -1
  4. package/dist/src/config.d.ts +20 -0
  5. package/dist/src/config.js +94 -0
  6. package/dist/src/config.js.map +1 -0
  7. package/dist/src/index.d.ts +3 -2
  8. package/dist/src/index.js +56 -27
  9. package/dist/src/index.js.map +1 -1
  10. package/dist/src/manifest.d.ts +20 -0
  11. package/dist/src/manifest.js +57 -0
  12. package/dist/src/manifest.js.map +1 -0
  13. package/dist/src/refresh.d.ts +30 -0
  14. package/dist/src/refresh.js +465 -0
  15. package/dist/src/refresh.js.map +1 -0
  16. package/dist/src/structured.d.ts +12 -0
  17. package/dist/src/structured.js +236 -0
  18. package/dist/src/structured.js.map +1 -0
  19. package/package.json +2 -2
  20. package/template/.speculo/README.md +12 -11
  21. package/template/.speculo/refresh-contract.json +30 -0
  22. package/template/canonical/canonical-specdev-goal-plan.md +436 -68
  23. package/template/skills/github-npm-ops/references/preflight-checklist.md +1 -1
  24. package/template/skills/source-code-zip/SKILL.md +568 -0
  25. package/template/skills/source-code-zip/scripts/zip_source_code.js +1363 -0
  26. package/template/workflows/person/runtime-contract.json +9 -0
  27. package/template/workflows/specdev/I-init-setup/I-init-setup.md +1 -1
  28. package/template/workflows/specdev/INDEX.md +7 -8
  29. package/template/workflows/specdev/common/skills/subagent-delivery/SKILL.md +88 -27
  30. package/template/workflows/specdev/common/skills/subagent-delivery/references/external-web-subagent.md +121 -9
  31. package/template/workflows/specdev/common/skills/subagent-delivery/references/native-subagent.md +10 -7
  32. package/template/workflows/specdev/common/skills/subagent-delivery/references/source-package.md +229 -9
  33. package/template/workflows/specdev/runtime-contract.json +26 -0
  34. package/dist/src/migrations.d.ts +0 -23
  35. package/dist/src/migrations.js +0 -1202
  36. package/dist/src/migrations.js.map +0 -1
  37. package/template/commands/migrate-runtime-state.md +0 -43
  38. package/template/skills/migrate-runtime-state/SKILL.md +0 -93
  39. package/template/skills/migrate-runtime-state/references/migration-contract.md +0 -64
  40. package/template/skills/migrate-runtime-state/scripts/migrate-runtime-state.mjs +0 -916
  41. package/template/skills/source-code-zip-skill/SKILL.md +0 -343
  42. package/template/skills/source-code-zip-skill/scripts/zip_source_code.py +0 -638
  43. package/template/workflows/specdev/common/skills/subagent-delivery/references/github-checkpoints.md +0 -24
@@ -1,17 +1,237 @@
1
- # Source Package
1
+ # External ZIP Package
2
2
 
3
- 外部 Agent 需要固定附件、私有上下文或受保护的未提交改动,且用户已授权目标 provider 与内容范围时加载。包位于调用方授权的临时位置;SpecDev 只在 Goal Plan Evidence 记录可迁移 locator、manifest 摘要和 hash。
3
+ 选择 `delivery_channel=external-web` 时加载。本 reference 规定 outbound return ZIP 的目录、内容、打包和持久化合同。它引用 `<Path>{roots.skills}/source-code-zip/SKILL.md</Path>` 及其单文件脚本 `<Path>{roots.skills}/source-code-zip/scripts/zip_source_code.js</Path>`;不得为打包执行 `npm install`,不得用另一套默认归档规则替换它。
4
4
 
5
- ## 范围与排除
5
+ ## 1. 根目录持久化不变量
6
6
 
7
- 包应包含理解、修改和验证 Ticket 所需的最小完整源码、直接依赖、构建配置、锁文件、schema、测试、项目 Agent 指令,以及 Spec/Ticket/ADR/CONTEXT 的相关摘录。
7
+ 所有外部交付 ZIP 必须位于项目根目录 `<Path>temp/</Path>` 下,使用以下可迁移布局:
8
8
 
9
- 排除版本控制内部数据、依赖缓存、构建产物、日志、数据库、转储、浏览器状态、真实用户数据、环境文件、token、cookie、私钥、证书私钥、验证码和恢复码。环境说明只保留无真实值的示例。
9
+ ```text
10
+ temp/subagent-delivery/{scope-id}/{task-id}/{dispatch-id}/
11
+ ├── outbound/
12
+ │ ├── staging/
13
+ │ │ ├── DISPATCH.md
14
+ │ │ ├── MANIFEST.json
15
+ │ │ ├── context/
16
+ │ │ └── source/
17
+ │ ├── {dispatch-id}.outbound.zip
18
+ │ └── {dispatch-id}.outbound.sha256
19
+ ├── SESSION.md
20
+ └── inbound/
21
+ └── {attempt-id}/
22
+ ├── raw/
23
+ ├── staging/
24
+ ├── extracted/
25
+ ├── {dispatch-id}.return.{attempt-id}.zip
26
+ ├── {dispatch-id}.return.{attempt-id}.sha256
27
+ └── ACCEPTANCE.md
28
+ ```
10
29
 
11
- ## 生成与核对
30
+ `scope-id`、`task-id`、`dispatch-id` 和 `attempt-id` 只使用 `[A-Za-z0-9._-]`,不得包含 `/`、`\`、`..`、盘符、控制字符或用户提供的未清洗路径。
12
31
 
13
- 优先从已提交 checkpoint 生成;包含受保护工作区改动时,manifest 必须列出基线和差异范围。使用仓库已有或可用的密钥扫描器,随后验证包可解压、文件清单、字节数和 SHA-256。
32
+ 以下位置不能作为最终 locator:操作系统临时目录、`os.tmpdir()`、`/tmp`、`%TEMP%`、浏览器默认瞬时下载目录、provider 会话缓存或聊天附件 URL。可以使用这些机制完成传输,但必须在 dispatch/accept 结束前把原始字节持久化到上述项目内目录。
14
33
 
15
- Manifest 至少记录 repository、branch、checkpoint、工作区状态、包 locator、size、SHA-256、secret scan、included、excluded workspace diff。源码变化后生成新 locator hash,不覆盖旧包或沿用旧 manifest。
34
+ 同一 locator 永不覆盖。发现目标已存在时创建新的 dispatch/attempt;不得使用 source-code-zip `--force` 掩盖标识冲突。dispatch/accept 不自动清理旧包。
16
35
 
17
- **完成标准**:包可完整读取,来源与范围可复现,不包含凭据、运行状态或真实用户数据。
36
+ ## 2. Outbound staging 内容
37
+
38
+ `outbound/staging/` 是由 Lead 主动整理的最小授权树,不是 repository 的无差别镜像。
39
+
40
+ ### 必需文件
41
+
42
+ `DISPATCH.md` 至少包含:
43
+
44
+ - dispatch identity、task kind、目标与成功定义;
45
+ - 固定 checkpoint、repository label、branch/workspace label;
46
+ - IN/OUT、已锁定决定、适用合同和依赖 Evidence 摘要;
47
+ - writable/read-only/shared 路径语义;外部通道没有本地写入所有权;
48
+ - 允许的联网域、URL 类型、工具、调用预算和停止条件;
49
+ - 禁止动作、敏感数据边界和 prompt-injection 规则;
50
+ - 按 task kind 定义的返回文件、字段、引用与未验证声明要求;
51
+ - Lead 本地验收将重新执行的检查。
52
+
53
+ `MANIFEST.json` 至少包含:
54
+
55
+ ```json
56
+ {
57
+ "schema": "speculo.subagent-delivery.packet/v1",
58
+ "dispatch_id": "...",
59
+ "task_id": "...",
60
+ "task_kind": "implementation|review|research|test-observation",
61
+ "delivery_channel": "external-web",
62
+ "created_at": "RFC-3339",
63
+ "repository_label": "...",
64
+ "branch": "...",
65
+ "base_checkpoint": "...",
66
+ "workspace_state": "clean|authorized-diff|snapshot",
67
+ "authorized_data": [],
68
+ "included": [],
69
+ "excluded": [],
70
+ "source_diff": null,
71
+ "secret_scan": {
72
+ "tool": "...",
73
+ "result": "pass|blocked",
74
+ "notes": "..."
75
+ }
76
+ }
77
+ ```
78
+
79
+ 归档 SHA-256 不写入归档内部的 `MANIFEST.json`,避免自引用;它写入相邻 `.sha256` 文件并记录到 Dispatch Packet/Evidence。
80
+
81
+ ### 可选内容
82
+
83
+ - `context/`:相关 Spec/Ticket/ADR/CONTEXT 摘要、项目 Agent 指令、接口合同、研究问题、已授权网页列表和无秘密的环境说明;
84
+ - `source/`:保持 repository-relative 路径的最小完整源码、直接依赖、schema、测试、构建配置和必要样例;
85
+ - `context/workspace.diff`:仅在用户明确授权发送受保护未提交改动时包含,并在 manifest 记录基线和差异范围;
86
+ - `context/expected-output/`:返回模板或 schema。
87
+
88
+ 纯公开网页 research 可以不含 `source/`,但仍需 `DISPATCH.md`、`MANIFEST.json` 和必要 `context/`。implementation/review 若缺少足以独立判断的源码或合同,不得靠 provider 猜测,应返回 blocked 或改用原生通道。
89
+
90
+ ## 3. 范围与排除
91
+
92
+ 只包含完成任务所需的最小完整信息。默认排除:
93
+
94
+ - 版本控制内部数据与远端凭据;
95
+ - 依赖缓存、虚拟环境、构建产物、覆盖率、日志、数据库、转储和临时文件;
96
+ - 浏览器 profile、cookie、local storage、会话 token、下载历史和截图缓存;
97
+ - 真实用户数据、生产数据、支持工单、邮件、聊天记录和未经授权的内部文档;
98
+ - `.env`、token、API key、cookie、私钥、证书私钥、keystore、验证码、恢复码和密码;
99
+ - 无关源码、无关测试、大型二进制、既有归档和可执行产物。
100
+
101
+ 环境说明只保留无真实值的示例。若 source-code-zip 默认安全规则会排除一个确有必要的 YAML、锁文件、媒体或其他文件,优先创建已脱敏的 Markdown/文本摘录并记录原始路径与遗漏影响;不得默认使用 `--no-default-ignore`。无法在不发送敏感/被排除内容的情况下完成任务时,不选择外部通道。
102
+
103
+ 使用 repository 已有或可用的 secret scanner 检查 staging;同时人工核对 manifest 与实际文件。无法合理确认没有秘密或真实用户数据时返回 blocked。
104
+
105
+ ## 4. 使用 source-code-zip 生成 outbound ZIP
106
+
107
+ 先确认 Node.js,再从项目根目录运行。以下示例中的变量必须替换为本次不可变标识:
108
+
109
+ ```bash
110
+ node --version
111
+
112
+ DELIVERY_ROOT="temp/subagent-delivery/${SCOPE_ID}/${TASK_ID}/${DISPATCH_ID}"
113
+ STAGING="${DELIVERY_ROOT}/outbound/staging"
114
+ ARCHIVE="${DELIVERY_ROOT}/outbound/${DISPATCH_ID}.outbound.zip"
115
+ ZIP_SCRIPT="speculo/skills/source-code-zip/scripts/zip_source_code.js"
116
+ ```
117
+
118
+ 若当前执行环境仍位于 template 源树而不是安装后的 workspace,按 `workspace.json` 中 `skills` 根别名的实际解析结果定位脚本,不硬编码另一个根。先创建 `outbound/staging/`、`outbound/` 与后续 inbound attempt 目录,并确认目标 ZIP 不存在。
119
+
120
+ 必须先预览:
121
+
122
+ ```bash
123
+ node "${ZIP_SCRIPT}" "${STAGING}" \
124
+ --all-files \
125
+ --contents-only \
126
+ --output "${ARCHIVE}" \
127
+ --dry-run \
128
+ --verbose
129
+ ```
130
+
131
+ 核对预览后,用完全相同的选择参数正式生成:
132
+
133
+ ```bash
134
+ node "${ZIP_SCRIPT}" "${STAGING}" \
135
+ --all-files \
136
+ --contents-only \
137
+ --output "${ARCHIVE}"
138
+ ```
139
+
140
+ 这里使用 `--all-files`,因为 staging 已由 Lead 精选,且必须纳入 `DISPATCH.md`、`MANIFEST.json`、patch 和普通项目文件;source-code-zip 的默认 IGNORE 仍然生效。使用 `--contents-only` 使 provider 在 ZIP 根目录直接看到权威文件。
141
+
142
+ 禁止:
143
+
144
+ - `--no-default-ignore`;
145
+ - `--force`;
146
+ - 正式命令与 dry-run 使用不同的 include/ignore 选择;
147
+ - 把输出 ZIP 放进 staging;
148
+ - 为运行脚本执行 npm/pnpm/yarn install;
149
+ - 在生成后手工修改 ZIP 而不生成新 dispatch/hash。
150
+
151
+ 生成后验证 ZIP 可读取、文件数、总字节数和清单,并计算 SHA-256。可以使用当前平台的可信 SHA-256 工具;仅有 Node.js 时可使用:
152
+
153
+ ```bash
154
+ node -e 'const fs=require("fs"),c=require("crypto");const p=process.argv[1],h=c.createHash("sha256"),s=fs.createReadStream(p);s.on("data",d=>h.update(d));s.on("error",e=>{console.error(e.message);process.exit(1)});s.on("end",()=>console.log(h.digest("hex")));' "${ARCHIVE}" \
155
+ > "${DELIVERY_ROOT}/outbound/${DISPATCH_ID}.outbound.sha256"
156
+ ```
157
+
158
+ 在 Packet、`SESSION.md` 和后续 Evidence 中记录 project-relative ZIP locator、size、SHA-256、secret scan、included/excluded 摘要和 workspace diff 摘要。只有完成这些记录后才能上传。
159
+
160
+ ## 5. Provider 返回与 return ZIP
161
+
162
+ ### Provider 直接下载 ZIP
163
+
164
+ 将下载的原始字节保存到唯一的:
165
+
166
+ ```text
167
+ temp/subagent-delivery/{scope-id}/{task-id}/{dispatch-id}/inbound/{attempt-id}/raw/
168
+ ```
169
+
170
+ 先计算原始下载 SHA-256,再检查归档目录。不要在保存前让浏览器自动解压,不要重用 provider 文件名覆盖旧文件。每个 attempt 仍必须产生确定名称的 `{dispatch-id}.return.{attempt-id}.zip`:若原始 ZIP 通过安全检查且已经符合返回结构,保持原始 ZIP 不变并把同一字节复制到确定名称;若结构不符合,先在隔离目录安全解包,只把允许的返回文件放入 inbound staging,再使用 source-code-zip 生成标准 return ZIP。两种情况都保留 `raw/` 中的原始字节、原始 hash 与标准 return ZIP/hash。
171
+
172
+ ### Provider 只返回文本或散列文件
173
+
174
+ 先原样保存到 `raw/`,再由 Lead 构建 `inbound/{attempt-id}/staging/`:
175
+
176
+ ```text
177
+ RETURN.md
178
+ candidate/ # implementation 可选
179
+ PATCH.diff # implementation 可选
180
+ FINDINGS.md # review 可选
181
+ RESEARCH.md # research 可选
182
+ SOURCES.json # research 可选
183
+ CHECKS.md # implementation/test-observation 可选
184
+ ```
185
+
186
+ `RETURN.md` 必须标明 `dispatch_id`、`attempt-id`、provider/session locator、原始响应 locator、捕获方式、provider 原始字段与 Lead 补写字段。Lead 补写使用 `captured_by_lead` 标识。
187
+
188
+ 使用同一个 source-code-zip Skill 预览并生成:
189
+
190
+ ```bash
191
+ RETURN_STAGING="${DELIVERY_ROOT}/inbound/${ATTEMPT_ID}/staging"
192
+ RETURN_ZIP="${DELIVERY_ROOT}/inbound/${ATTEMPT_ID}/${DISPATCH_ID}.return.${ATTEMPT_ID}.zip"
193
+
194
+ node "${ZIP_SCRIPT}" "${RETURN_STAGING}" \
195
+ --all-files \
196
+ --contents-only \
197
+ --output "${RETURN_ZIP}" \
198
+ --dry-run \
199
+ --verbose
200
+
201
+ node "${ZIP_SCRIPT}" "${RETURN_STAGING}" \
202
+ --all-files \
203
+ --contents-only \
204
+ --output "${RETURN_ZIP}"
205
+ ```
206
+
207
+ 随后生成相邻 `.sha256`。不得用本地重打包抹掉 provider 原始响应或补造其未给出的事实。
208
+
209
+ ## 6. 安全检查与解包
210
+
211
+ 外部 ZIP 是不可信输入。Lead 必须先枚举中央目录并验证,再解压到本 attempt 的 `extracted/`,绝不直接解压到 repository/worktree。
212
+
213
+ 至少拒绝:
214
+
215
+ - 绝对路径、盘符路径、UNC 路径、NUL、空文件名;
216
+ - 规范化后包含 `..`、逃出 extraction root 或使用混淆分隔符的路径;
217
+ - 符号链接、硬链接、设备文件和其他非常规条目;
218
+ - 重复路径、Unicode/大小写规范化冲突、文件与目录同名冲突;
219
+ - 超过 Packet 上限的条目数、单文件大小、总解压大小或压缩比;
220
+ - 未授权的嵌套归档、可执行文件、脚本副作用或秘密材料。
221
+
222
+ 安全解包只证明归档结构可接受,不证明内容正确。Lead 仍需对照 dispatch identity、outbound manifest、checkpoint、IN/OUT、返回 schema 和实际 diff;任何外部命令/测试声明保持 `unverified`,直到本地复现。
223
+
224
+ ## 7. 版本、修正与清理
225
+
226
+ 以下任一变化都生成新的 `dispatch-id`、staging、outbound ZIP 和 hash:
227
+
228
+ - base/source checkpoint;
229
+ - IN/OUT、合同、目标或返回 schema;
230
+ - 发送内容或用户授权范围;
231
+ - provider、数据保留边界、允许域或工具权限。
232
+
233
+ 固定输入不变但重新请求答案时生成新的 `attempt-id` 和 return ZIP。任何包都不得覆盖;`ACCEPTANCE.md` 记录 accepted/rejected/blocked、Lead 本地验证、未验证项和恢复条件。
234
+
235
+ `temp/subagent-delivery/` 是持久化交付证据,不在 dispatch/accept 中自动删除。清理必须由 Lead 在任务外显式决定,并确保调用方 Evidence 不再依赖唯一 locator。
236
+
237
+ **完成标准**:每个外部输入与返回都能由 project-relative locator、manifest、size、SHA-256、dispatch/attempt identity 和 Lead 验收记录唯一定位;所有 ZIP 均持久化在项目根目录 `temp/` 下。
@@ -0,0 +1,26 @@
1
+ {
2
+ "schema_version": 1,
3
+ "workflow": "specdev",
4
+ "config": {
5
+ "path": ".speculo/specdev/config.json",
6
+ "template": "workflows/specdev/I-init-setup/config-template.json",
7
+ "baseline": ".speculo/baselines/workflows/specdev/config.json",
8
+ "schema_version": 5,
9
+ "optional": true,
10
+ "additional_properties": {
11
+ "": false,
12
+ "git": false,
13
+ "execution": false,
14
+ "verification": true,
15
+ "planning": true
16
+ }
17
+ },
18
+ "structured_state": [
19
+ ".speculo/specdev/status.json",
20
+ ".speculo/specdev/changes/*/.status.json",
21
+ ".speculo/specdev/archive/*/*/.status.json",
22
+ ".speculo/specdev/changes/*/goal-plan.md",
23
+ ".speculo/specdev/archive/*/*/goal-plan.md"
24
+ ],
25
+ "opaque_default": "preserve-byte-for-byte"
26
+ }
@@ -1,23 +0,0 @@
1
- export type MigrationStatus = "not-required" | "migrated" | "pending";
2
- export type MigrationBlocker = {
3
- code: string;
4
- path: string;
5
- message: string;
6
- };
7
- export type RuntimeMigrationResult = {
8
- status: MigrationStatus;
9
- sourceVersion: string | null;
10
- targetVersion: string;
11
- backupPath: string | null;
12
- blockers: MigrationBlocker[];
13
- };
14
- export type RuntimeMigrationOptions = {
15
- packageRoot: string;
16
- previousRoot: string;
17
- stagedRoot: string;
18
- selectedWorkflowIds: string[];
19
- unselectedWorkflowIds: string[];
20
- };
21
- export declare function assertNoPendingMigration(previousRoot: string): Promise<void>;
22
- export declare function migrateRuntimeState(options: RuntimeMigrationOptions): Promise<RuntimeMigrationResult>;
23
- export declare function initializeRuntimeManifest(packageRoot: string, stagedRoot: string, workflowIds: string[]): Promise<RuntimeMigrationResult>;