add-coder 0.2.8 → 0.3.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.
- package/README.md +66 -27
- package/dist/index.js +158 -11
- package/package.json +9 -12
- package/templates/.add-coder-src-hash.json +256 -0
- package/templates/adapters/claude/hooks/doc-format-guard.sh +17 -0
- package/templates/adapters/claude/hooks/lib/notify.sh +34 -0
- package/templates/adapters/claude/hooks/pre-tool-use.sh +26 -7
- package/templates/adapters/claude/hooks/prompt-submit.sh +16 -0
- package/templates/adapters/codex/hooks/doc-format-guard.sh +17 -0
- package/templates/adapters/codex/hooks/lib/notify.sh +34 -0
- package/templates/adapters/codex/hooks/pre-tool-use.sh +48 -18
- package/templates/adapters/codex/hooks/prompt-submit.sh +16 -0
- package/templates/adapters/qoder/hooks/doc-format-guard.sh +17 -0
- package/templates/adapters/qoder/hooks/lib/notify.sh +34 -0
- package/templates/adapters/qoder/hooks/pre-tool-use.sh +28 -9
- package/templates/adapters/qoder/hooks/prompt-submit.sh +18 -1
- package/templates/adapters/trae/hooks/doc-format-guard.sh +17 -0
- package/templates/adapters/trae/hooks/lib/notify.sh +34 -0
- package/templates/adapters/trae/hooks/pre-tool-use.sh +48 -18
- package/templates/adapters/trae/hooks/prompt-submit.sh +16 -0
- package/templates/adapters/vscode/hooks/doc-format-guard.sh +16 -0
- package/templates/adapters/vscode/hooks/lib/notify.sh +34 -0
- package/templates/adapters/vscode/hooks/pre-tool-use.sh +26 -7
- package/templates/adapters/vscode/hooks/prompt-submit.sh +16 -0
- package/templates/core/hooks/doc-format-guard.sh +17 -0
- package/templates/core/hooks/lib/notify.sh +34 -0
- package/templates/core/hooks/pre-tool-use.sh +48 -18
- package/templates/core/hooks/prompt-submit.sh +16 -0
- package/templates/core/reviews/.gitkeep +0 -0
- package/templates/core/scripts/mcp-server/elicitation/confirm.ts +30 -0
- package/templates/core/scripts/mcp-server/elicitation/index.ts +1 -0
- package/templates/core/scripts/mcp-server/index.ts +15 -0
- package/templates/core/scripts/mcp-server/notifications/hitl.ts +49 -0
- package/templates/core/scripts/mcp-server/notifications/hook.ts +268 -0
- package/templates/core/scripts/mcp-server/notifications/index.ts +8 -0
- package/templates/core/scripts/mcp-server/resources/add-coder-version.ts +25 -0
- package/templates/core/scripts/mcp-server/resources/add-state.ts +44 -0
- package/templates/core/scripts/mcp-server/resources/hook-events-report.ts +104 -0
- package/templates/core/scripts/mcp-server/resources/index.ts +12 -0
- package/templates/core/scripts/mcp-server/resources/round-task.ts +22 -0
- package/templates/core/scripts/mcp-server/sampling/index.ts +1 -0
- package/templates/core/scripts/mcp-server/sampling/review.ts +47 -0
- package/templates/core/scripts/mcp-server/shared/env.ts +26 -0
- package/templates/core/scripts/mcp-server/shared/fs.ts +40 -0
- package/templates/core/scripts/mcp-server/shared/prisma.ts +35 -0
- package/templates/core/scripts/mcp-server/shared/response.ts +9 -0
- package/templates/core/scripts/mcp-server/tasks/index.ts +2 -0
- package/templates/core/scripts/mcp-server/tasks/runner.ts +20 -0
- package/templates/core/scripts/mcp-server/tasks/store.ts +20 -0
- package/templates/core/scripts/mcp-server/tools/audit.ts +73 -0
- package/templates/core/scripts/mcp-server/tools/context.ts +337 -0
- package/templates/core/scripts/mcp-server/tools/docs.ts +42 -0
- package/templates/core/scripts/mcp-server/tools/gateway.ts +135 -0
- package/templates/core/scripts/mcp-server/tools/hook-event-report.ts +92 -0
- package/templates/core/scripts/mcp-server/tools/index.ts +17 -0
- package/templates/core/scripts/mcp-server/tools/quality.ts +93 -0
- package/templates/core/scripts/mcp-server/types.ts +7 -0
- package/templates/core/scripts/mcp-server.ts +8 -3455
package/README.md
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
# add-coder
|
|
2
2
|
|
|
3
|
+
Make 0.75 into one.
|
|
4
|
+
|
|
3
5
|
> 🀄中文 | 🔤[English](#-english-readme)
|
|
4
6
|
|
|
5
7
|
**AI 代码治理的落地方案** — [codein2027](https://github.com/xiaomingming92/codein2027) 快速构建 ADD 编程范式的完整脚手架。以「审计即基础设施」为核心,彻底打破编程过程黑盒与跨轮失忆,让编程范式进化为可审计、可追溯、可收敛的新时代。 [NPM](https://www.npmjs.com/package/add-coder) · [GitHub](https://github.com/xiaomingming92/add-coder)
|
|
6
8
|
|
|
7
9
|
|
|
8
10
|
> 🧭 **从零上手实操?** 请参见 [GUIDE.md](https://github.com/xiaomingming92/add-coder/blob/main/GUIDE.md) — 包含触发词速查、需求转 Plan、完整链路演练。
|
|
11
|
+
>
|
|
12
|
+
> 🔄 **add-coder 升级后更新模板?** `npx add-coder sync --adapter=qoder --patch` — 实操看 [GUIDE.md §版本升级](#) | 原理看 [DEVELOPMENT.md](https://github.com/xiaomingming92/add-coder/blob/main/DEVELOPMENT.md)
|
|
9
13
|
|
|
10
14
|
```bash
|
|
11
15
|
npx add-coder init
|
|
@@ -27,7 +31,22 @@ npx add-coder init
|
|
|
27
31
|
| 审计靠开发者自觉记录 | **MCP 审计工具链** 自动记录每次操作,系统闸门强制检查 |
|
|
28
32
|
| 无关联性 | 审计事件天然关联 Plan → Spec → Task → Step → Tool Call,形成完整证据链 |
|
|
29
33
|
|
|
30
|
-
### ②
|
|
34
|
+
### ② Caijuehub 规则引擎:业务规则直驱代码
|
|
35
|
+
|
|
36
|
+
Caijuehub 是 add-coder 历史上第一个实现 **TOML 规则声明 → 自动生成策略 → 业务代码消费** 的裁决引擎。以 sync --patch 为例:
|
|
37
|
+
|
|
38
|
+
```toml
|
|
39
|
+
# sync-rules.toml — 改这里,不改代码
|
|
40
|
+
[patch]
|
|
41
|
+
on_conflict = "interactive" # 内容冲突时:交互勾选
|
|
42
|
+
on_missing = "write" # 文件缺失时:静默写入
|
|
43
|
+
[version]
|
|
44
|
+
on_upgrade = "baseline" # 版本升级时:自动覆盖
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
`npm run generate` 后,策略直驱 sync.ts 行为。**人类从"追踪散落的 if"升级为"读一张决策表"**——认知负担从 O(N×M) 降为 O(1)。产品经理、市场人员改 TOML 即可把控软件能力,决策权集中、单一入口绕不过去。更进一步:当业务规则以声明式集中管理,**AI Agent 可大规模索引、检索、修改规则**——人机协作不再局限于对话生成代码,而是共同操作同一张决策表。这是 [codein2027 集中裁决层理论](https://github.com/xiaomingming92/codein2027) 的第一个工程落地。详见 [docs/caijuehub.md](./docs/caijuehub.md)。
|
|
48
|
+
|
|
49
|
+
### ③ Prompt Cache 原生友好 — 月费 ¥218,节省 98%
|
|
31
50
|
|
|
32
51
|
ADD 范式不仅是方法论——它的结构化 Step 流程天然适配 DeepSeek Prompt Cache 的前缀匹配机制,带来极致的 Token 成本效率。**实测数据验证**:
|
|
33
52
|
|
|
@@ -46,7 +65,7 @@ ADD 范式 + Qoder: cache 命中率 99.31%, 每次请求 MISS 仅 2,426 toke
|
|
|
46
65
|
|
|
47
66
|
> 📊 [完整分析报告](./docs/ADD范式缓存命中分析报告.md) — 含 4 张 Mermaid 图表、17 天逐日数据、跨 IDE 对比与成本建模。
|
|
48
67
|
|
|
49
|
-
###
|
|
68
|
+
### ④ 门禁驱动,而非自由对话
|
|
50
69
|
|
|
51
70
|
传统 AI coding 是「你说我做」,质量完全依赖 LLM 当天状态。add-coder 在架构中嵌入了 **双质量闸门**:
|
|
52
71
|
|
|
@@ -57,7 +76,7 @@ RAHS (Runtime Architecture Health Score) — 运行时架构健康度,< 90% BL
|
|
|
57
76
|
|
|
58
77
|
这不是「建议」,是**架构阻断** — 不通过闸门的 Step 无法推进到下一步。
|
|
59
78
|
|
|
60
|
-
###
|
|
79
|
+
### ⑤ 跨轮记忆,而非每轮失忆
|
|
61
80
|
|
|
62
81
|
AI 对话的致命缺陷:上次讨论的架构决策、已修复的 Bug、达成的约定,下轮对话全部遗忘。add-coder 在架构层面解决:
|
|
63
82
|
|
|
@@ -65,7 +84,7 @@ AI 对话的致命缺陷:上次讨论的架构决策、已修复的 Bug、达
|
|
|
65
84
|
- **Plan 索引** — 所有 Plan 通过 `index.md` 集中索引,支持模糊匹配快速定位
|
|
66
85
|
- **DevLog 时序记录** — 每一步操作写入 `{YYYY-MM}/{DD}/` 时间轴,可回溯任意历史状态
|
|
67
86
|
|
|
68
|
-
###
|
|
87
|
+
### ⑥ Policy-Update-Loop:治理自我进化(脚手架不包含此架构能力,接下来会给到DEMO仓库让大家更好理解Policy-Update-Loop和Report体系)
|
|
69
88
|
|
|
70
89
|
不是静态模板,而是**闭环自适应系统**:
|
|
71
90
|
|
|
@@ -75,7 +94,7 @@ AI 对话的致命缺陷:上次讨论的架构决策、已修复的 Bug、达
|
|
|
75
94
|
|
|
76
95
|
运行时产生的 Report 会反过来更新 governance rules,实现治理策略的持续进化。
|
|
77
96
|
|
|
78
|
-
###
|
|
97
|
+
### ⑦ 多 IDE 的 Hook 即治理层
|
|
79
98
|
|
|
80
99
|
hook 不是「通知推送」,而是 **ADD 范式在 IDE agent 生命周期中的 17 个确定性治理卡位**。每个 IDE(Claude Code / Qoder CN / VS Code Copilot / Trae / Codex)有各自的 hook 机制,但治理逻辑统一——架构一致,适配层不同。
|
|
81
100
|
|
|
@@ -89,6 +108,8 @@ hook 不是「通知推送」,而是 **ADD 范式在 IDE agent 生命周期中
|
|
|
89
108
|
|
|
90
109
|
> 实施 Plan: [add-coder-hook-full-alignment-plan-v1](./.qoder/plans/2026-07/17/add-coder-hook-full-alignment-plan-v1.md) | 触发源: [GitHub Issue #6](https://github.com/xiaomingming92/add-coder/issues/6)
|
|
91
110
|
|
|
111
|
+
|
|
112
|
+
|
|
92
113
|
---
|
|
93
114
|
|
|
94
115
|
## 快速开始
|
|
@@ -158,16 +179,16 @@ npx add-coder init
|
|
|
158
179
|
```
|
|
159
180
|
MCP 能力 方向 当前状态 说明
|
|
160
181
|
─────────────────────────────────────────────────────────────
|
|
161
|
-
Tools Client→Server ✅ 已实现
|
|
162
|
-
Resources+Sub Client←Server
|
|
163
|
-
Notifications Server→Client
|
|
164
|
-
Sampling Server→Client
|
|
182
|
+
Tools Client→Server ✅ 已实现 18 个审计与治理工具(pull 模式)
|
|
183
|
+
Resources+Sub Client←Server ✅ 已实现 8 个端点 Plan/Review/Route/Task/Hook 状态推送
|
|
184
|
+
Notifications Server→Client ✅ 已实现 HITL 就绪 + Hook 拦截事件推送
|
|
185
|
+
Sampling Server→Client ✅ 已实现 服务端回调 AI 生成 Review (HITL 两步法)
|
|
165
186
|
── 横切 ──
|
|
166
|
-
Elicitation Server→Client
|
|
167
|
-
Tasks (实验性) 双向
|
|
187
|
+
Elicitation Server→Client ✅ 已实现 向用户请求 HITL 确认/风险输入
|
|
188
|
+
Tasks (实验性) 双向 ✅ 已实现 长任务持久化 + 状态追踪
|
|
168
189
|
```
|
|
169
190
|
|
|
170
|
-
**当前已实现的
|
|
191
|
+
**当前已实现的 18 个 Tools**:
|
|
171
192
|
|
|
172
193
|
| 工具 | 用途 | 触发场景 |
|
|
173
194
|
|------|------|---------|
|
|
@@ -180,6 +201,7 @@ Tasks (实验性) 双向 🔜 本轮 长任务持久化 +
|
|
|
180
201
|
| `check_add_route_status` | add-route 文件存在性校验 | Step 3 前 |
|
|
181
202
|
| `check_spec_sync` | Spec 文档勾选状态与代码一致性 | Spec 执行后 |
|
|
182
203
|
| `find_related_docs` | 检索相关架构/规范文档 | 语境理解 |
|
|
204
|
+
| `get_hook_events` | 查询 Hook 拦截事件(planKeyword/hook/时间过滤+分组) | 治理审计、阈值告警 |
|
|
183
205
|
|
|
184
206
|
> 完整六能力架构设计见 [MCP 重构 Plan](.qoder/plans/2026-07/23/add-coder-mcp-restructure-plan-v1.md)。
|
|
185
207
|
|
|
@@ -246,11 +268,12 @@ Tasks (实验性) 双向 🔜 本轮 长任务持久化 +
|
|
|
246
268
|
|
|
247
269
|
## 🎬 预告
|
|
248
270
|
|
|
249
|
-
| 计划 | 说明
|
|
250
|
-
|
|
251
|
-
| Demo 仓库演示 | 提供完整示例仓库,展示 Policy-Update-Loop 与 Report 体系的端到端闭环实践
|
|
252
|
-
| MCP 能力重构 | MCP 工具链架构升级,提升审计与门禁工具的可扩展性和独立部署能力 |
|
|
253
|
-
|
|
|
271
|
+
| 计划 | 说明 |备注|
|
|
272
|
+
|------|------|------|
|
|
273
|
+
| Demo 仓库演示 | 提供完整示例仓库,展示 Policy-Update-Loop 与 Report 体系的端到端闭环实践 |--|
|
|
274
|
+
| MCP 能力重构 | ✅ v0.2.9 MCP 工具链架构升级,提升审计与门禁工具的可扩展性和独立部署能力 | 2026-07/23/add-coder-mcp-restructure-plan-v1.md |
|
|
275
|
+
| Hook 通知升级 | ✅ v0.2.9 Hook 拦截事件 jsonl → fs.watch → record_dev_operation 落库 + Notification + 治理信号 | 2026-07/24/add-coder-hook-notify-upgrade-plan-v1.md |
|
|
276
|
+
| 对话记忆增强 | 长期项目知识记忆和plan级别的稀疏记忆 |--|
|
|
254
277
|
|
|
255
278
|
|
|
256
279
|
|
|
@@ -282,7 +305,22 @@ Traditional AI development: Chat → Generate code → Dig through chat history
|
|
|
282
305
|
| Auditing relies on developer discipline | The **MCP audit toolchain** automatically records every operation; system gateways enforce checks |
|
|
283
306
|
| No traceability | Audit events are naturally linked: Plan → Spec → Task → Step → Tool Call, forming a complete evidence chain |
|
|
284
307
|
|
|
285
|
-
### ②
|
|
308
|
+
### ② Caijuehub Rule Engine: Business Rules Drive Code
|
|
309
|
+
|
|
310
|
+
Caijuehub is add-coder's first implementation of **TOML declaration → auto-generated strategy → business code consumption**. Using sync --patch as an example:
|
|
311
|
+
|
|
312
|
+
```toml
|
|
313
|
+
# sync-rules.toml — edit here, not code
|
|
314
|
+
[patch]
|
|
315
|
+
on_conflict = "interactive" # content conflict → interactive checkbox
|
|
316
|
+
on_missing = "write" # file missing → silent write
|
|
317
|
+
[version]
|
|
318
|
+
on_upgrade = "baseline" # version upgrade → auto overwrite
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
After `npm run generate`, the strategy directly drives sync.ts behavior. **Humans upgrade from "hunting scattered if-statements" to "reading a decision table"** — cognitive load drops from O(N×M) to O(1). Product managers and marketers edit TOML to control software capabilities, with decisions centralized at a single entry point that cannot be bypassed. Furthermore: when business rules are declaratively centralized, **AI Agents can index, search, and modify rules at scale** — human-AI collaboration extends beyond conversation-driven code generation to jointly operating the same decision table. This is the first engineering implementation of [codein2027's Centralized Decision Layer theory](https://github.com/xiaomingming92/codein2027). Details: [docs/caijuehub.md](./docs/caijuehub.md).
|
|
322
|
+
|
|
323
|
+
### ③ Prompt Cache Native — ¥218/mo, 98% Savings
|
|
286
324
|
|
|
287
325
|
The ADD paradigm isn't just methodology — its structured Step workflow naturally aligns with DeepSeek's Prompt Cache prefix-matching mechanism, delivering extreme token cost efficiency. **Real-world billing validation**:
|
|
288
326
|
|
|
@@ -301,7 +339,7 @@ ADD paradigm + Qoder: cache hit rate 99.31%, only 2,426 MISS tokens/req
|
|
|
301
339
|
|
|
302
340
|
> 📊 [Full analysis report](./docs/ADD范式缓存命中分析报告.md) — 4 Mermaid diagrams, 17-day daily data, cross-IDE comparison, and cost modeling.
|
|
303
341
|
|
|
304
|
-
###
|
|
342
|
+
### ④ Gateway-Driven, Not Free-Form Conversation
|
|
305
343
|
|
|
306
344
|
Traditional AI coding is "you say, I do" — quality depends entirely on the LLM's state that day. add-coder embeds **dual quality gateways** into the architecture:
|
|
307
345
|
|
|
@@ -312,7 +350,7 @@ RAHS (Runtime Architecture Health Score) — Runtime architecture health, < 90%
|
|
|
312
350
|
|
|
313
351
|
These are not "suggestions" — they are **architectural blocks**. A Step cannot advance without passing its gateway.
|
|
314
352
|
|
|
315
|
-
###
|
|
353
|
+
### ⑤ Cross-Session Memory, Not Per-Session Amnesia
|
|
316
354
|
|
|
317
355
|
The fatal flaw of AI conversations: architectural decisions from last session, bugs fixed, agreements reached — all forgotten in the next conversation. add-coder solves this at the architecture level:
|
|
318
356
|
|
|
@@ -320,7 +358,7 @@ The fatal flaw of AI conversations: architectural decisions from last session, b
|
|
|
320
358
|
- **Plan Index** — All Plans are centrally indexed via `index.md`, supporting fuzzy-match quick lookup
|
|
321
359
|
- **DevLog Timeline** — Every operation is written to the `{YYYY-MM}/{DD}/` timeline, enabling full historical state traceability
|
|
322
360
|
|
|
323
|
-
###
|
|
361
|
+
### ⑥ Policy-Update-Loop: Self-Evolving Governance (the scaffold itself does not include this architectural capability; a DEMO repo will be provided next to better illustrate the Policy-Update-Loop and Report system)
|
|
324
362
|
|
|
325
363
|
Not a static template, but a **closed-loop adaptive system**:
|
|
326
364
|
|
|
@@ -330,7 +368,7 @@ Execute → Audit → Boundary Report → Rule Adjustment → Next Execution
|
|
|
330
368
|
|
|
331
369
|
Runtime-generated Reports feed back into governance rules, enabling continuous evolution of governance strategies.
|
|
332
370
|
|
|
333
|
-
###
|
|
371
|
+
### ⑦ Multi-IDE Hooks as the Governance Layer
|
|
334
372
|
|
|
335
373
|
Hooks are not "notification push" — they are the **IDE runtime interception layer**:
|
|
336
374
|
|
|
@@ -497,10 +535,11 @@ npx add-coder init
|
|
|
497
535
|
|
|
498
536
|
## 🎬 Coming Soon
|
|
499
537
|
|
|
500
|
-
| Plan | Description |
|
|
501
|
-
|
|
502
|
-
| Demo Repo |
|
|
503
|
-
| MCP Restructure |
|
|
504
|
-
|
|
|
538
|
+
| Plan | Description | remark |
|
|
539
|
+
|------|-------------|:---:|
|
|
540
|
+
| Demo Repo | Full example repository showcasing Policy-Update-Loop with Report system end-to-end | 📋 |
|
|
541
|
+
| ~~MCP Restructure~~ | ✅ v0.2.9 3,467-line monolith → 27 modules covering all six MCP primitives | 2026-07/23/... |
|
|
542
|
+
| ~~Hook Notification Upgrade~~ | ✅ v0.2.9 ~36 files: hook intercept → jsonl → fs.watch → record_dev_operation | 2026-07/24/... |
|
|
543
|
+
| Memory Enhancement | Long-term project knowledge memory and plan-level sparse memory | 📋 |
|
|
505
544
|
|
|
506
545
|
---
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/cli/index.ts
|
|
4
|
-
import { readFileSync as
|
|
4
|
+
import { readFileSync as readFileSync7 } from "fs";
|
|
5
5
|
import { Command } from "commander";
|
|
6
6
|
|
|
7
7
|
// src/caijuehub/strategies/detect.strategy.ts
|
|
@@ -155,7 +155,7 @@ function renderList(items, selected, startIdx, height) {
|
|
|
155
155
|
`;
|
|
156
156
|
}
|
|
157
157
|
out += `
|
|
158
|
-
\u547D\u4EE4: [a]\u5168\
|
|
158
|
+
\u547D\u4EE4: [a]\u5168\u90E8\u8DF3\u8FC7 [A]\u5168\u90E8\u8986\u76D6 [1-${items.length}]\u5207\u6362 [Enter]\u786E\u8BA4 [q]\u9000\u51FA
|
|
159
159
|
`;
|
|
160
160
|
return out;
|
|
161
161
|
}
|
|
@@ -197,12 +197,12 @@ async function selectFiles(projectRoot, files) {
|
|
|
197
197
|
draw();
|
|
198
198
|
});
|
|
199
199
|
rl.on("line", (input2) => {
|
|
200
|
-
const trimmed = input2.trim()
|
|
201
|
-
if (trimmed === "q" || trimmed === "quit") {
|
|
200
|
+
const trimmed = input2.trim();
|
|
201
|
+
if (trimmed === "q" || trimmed.toLowerCase() === "quit") {
|
|
202
202
|
rl.close();
|
|
203
|
-
} else if (trimmed === "
|
|
203
|
+
} else if (trimmed === "A") {
|
|
204
204
|
items.forEach((_, i) => selected.add(i));
|
|
205
|
-
} else if (trimmed === "
|
|
205
|
+
} else if (trimmed === "a") {
|
|
206
206
|
selected.clear();
|
|
207
207
|
} else {
|
|
208
208
|
const num = parseInt(trimmed, 10);
|
|
@@ -444,7 +444,7 @@ var PRISMA_CONFIG = {
|
|
|
444
444
|
autoGenerate: true,
|
|
445
445
|
migrationName: "add_workflow_init",
|
|
446
446
|
schemaArg: "--schema=prisma/",
|
|
447
|
-
requiresUserModel:
|
|
447
|
+
requiresUserModel: true
|
|
448
448
|
};
|
|
449
449
|
function ensurePrismaConfig(projectRoot) {
|
|
450
450
|
const configPath = resolve3(projectRoot, "prisma.config.ts");
|
|
@@ -629,6 +629,7 @@ async function injectPrisma2(projectRoot, options = {}) {
|
|
|
629
629
|
|
|
630
630
|
// src/cli/commands/init.ts
|
|
631
631
|
import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync6, mkdirSync as mkdirSync3, copyFileSync as copyFileSync2, readdirSync as readdirSync2 } from "fs";
|
|
632
|
+
import { createHash } from "crypto";
|
|
632
633
|
import { resolve as resolve5 } from "path";
|
|
633
634
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
634
635
|
import { createConnection } from "net";
|
|
@@ -893,6 +894,33 @@ async function renderAndWrite(ctx) {
|
|
|
893
894
|
for (const [p, c] of claudeFiles) allFiles.set(p, c);
|
|
894
895
|
console.log(`claude adapter (via Agent Host): ${claudeFiles.size} \u6587\u4EF6`);
|
|
895
896
|
}
|
|
897
|
+
for (const d of [".add", magicDir]) {
|
|
898
|
+
const reviewsDir = resolve5(projectRoot, d, "reviews");
|
|
899
|
+
if (!existsSync6(reviewsDir)) {
|
|
900
|
+
if (dry) {
|
|
901
|
+
console.log(`[dry-run] \u5C06\u521B\u5EFA ${reviewsDir}/`);
|
|
902
|
+
} else {
|
|
903
|
+
mkdirSync3(reviewsDir, { recursive: true });
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
if (!dry) {
|
|
908
|
+
const hashMap = {};
|
|
909
|
+
let npmVer = "";
|
|
910
|
+
try {
|
|
911
|
+
npmVer = JSON.parse(readFileSync5(resolve5(projectRoot, "node_modules", "add-coder", "templates", ".add-coder-src-hash.json"), "utf-8"))._version ?? "";
|
|
912
|
+
} catch {
|
|
913
|
+
}
|
|
914
|
+
for (const [rp, c] of allFiles) {
|
|
915
|
+
hashMap[rp] = createHash("sha256").update(c).digest("hex").slice(0, 8);
|
|
916
|
+
}
|
|
917
|
+
const hashOut = resolve5(projectRoot, magicDir, ".add-coder-hash.json");
|
|
918
|
+
writeFileSync3(hashOut, JSON.stringify(hashMap, null, 2) + "\n", "utf-8");
|
|
919
|
+
if (npmVer) {
|
|
920
|
+
writeFileSync3(resolve5(projectRoot, magicDir, ".add-coder-version"), npmVer + "\n", "utf-8");
|
|
921
|
+
}
|
|
922
|
+
console.log(`hash: ${Object.keys(hashMap).length} entries \u2192 ${magicDir}/.add-coder-hash.json`);
|
|
923
|
+
}
|
|
896
924
|
return await writeFiles2(projectRoot, allFiles, { force: options.force, dryRun: options.dryRun });
|
|
897
925
|
}
|
|
898
926
|
async function deployDatabase(ctx) {
|
|
@@ -986,8 +1014,30 @@ function finalize(ctx, result) {
|
|
|
986
1014
|
}
|
|
987
1015
|
|
|
988
1016
|
// src/cli/commands/sync.ts
|
|
989
|
-
import { existsSync as existsSync7 } from "fs";
|
|
1017
|
+
import { existsSync as existsSync7, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "fs";
|
|
990
1018
|
import { resolve as resolve6 } from "path";
|
|
1019
|
+
import { createHash as createHash2 } from "crypto";
|
|
1020
|
+
|
|
1021
|
+
// src/caijuehub/strategies/sync.strategy.ts
|
|
1022
|
+
var SYNC_CONFIG = {
|
|
1023
|
+
PATCH_GUARD: [/[/]plans[/]/, /[/]specs[/]/, /[/]reviews[/]/],
|
|
1024
|
+
HASH_OUTPUT_FILE: ".add-coder-hash.json",
|
|
1025
|
+
HASH_SRC_FILE: "templates/.add-coder-src-hash.json",
|
|
1026
|
+
HASH_HEX_LENGTH: 8,
|
|
1027
|
+
PATCH_ON_MISSING: "write",
|
|
1028
|
+
PATCH_ON_CONFLICT: "interactive",
|
|
1029
|
+
PATCH_ON_SAME: "skip",
|
|
1030
|
+
VERSION_ON_FIRST: "baseline",
|
|
1031
|
+
VERSION_ON_UPGRADE: "baseline",
|
|
1032
|
+
VERSION_ON_HASH_LOST: "conflict",
|
|
1033
|
+
VERSION_SENTINEL: ".add-coder-version",
|
|
1034
|
+
DEFAULT_ON_MISSING: "write",
|
|
1035
|
+
DEFAULT_ON_EXISTING: "skip",
|
|
1036
|
+
PROMPT_FULL: "\u6240\u6709 ADD \u6A21\u677F\u6587\u4EF6\u5DF2\u5C31\u4F4D\u3002\u4F7F\u7528 --patch \u66F4\u65B0\u5DF2\u6709\u6587\u4EF6\u3002",
|
|
1037
|
+
PROMPT_PATCH_DONE: "\u6240\u6709 ADD \u6A21\u677F\u6587\u4EF6\u5DF2\u662F\u6700\u65B0\u3002"
|
|
1038
|
+
};
|
|
1039
|
+
|
|
1040
|
+
// src/cli/commands/sync.ts
|
|
991
1041
|
var ADAPTER_RENDERERS2 = {
|
|
992
1042
|
claude: renderAdapter,
|
|
993
1043
|
qoder: renderAdapter2,
|
|
@@ -1010,6 +1060,34 @@ function resolveAdapter2(projectRoot, specified) {
|
|
|
1010
1060
|
console.log("\u672A\u68C0\u6D4B\u5230 IDE \u73AF\u5883\uFF0C\u9ED8\u8BA4 qoder");
|
|
1011
1061
|
return "qoder";
|
|
1012
1062
|
}
|
|
1063
|
+
function isUserData(p) {
|
|
1064
|
+
return SYNC_CONFIG.PATCH_GUARD.some((r) => r.test(p));
|
|
1065
|
+
}
|
|
1066
|
+
function hash8(c) {
|
|
1067
|
+
return createHash2("sha256").update(c).digest("hex").slice(0, SYNC_CONFIG.HASH_HEX_LENGTH);
|
|
1068
|
+
}
|
|
1069
|
+
function loadHashFile(root, magic) {
|
|
1070
|
+
try {
|
|
1071
|
+
return JSON.parse(readFileSync6(resolve6(root, magic, SYNC_CONFIG.HASH_OUTPUT_FILE), "utf-8"));
|
|
1072
|
+
} catch {
|
|
1073
|
+
return {};
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
function loadVersionFile(root, magic) {
|
|
1077
|
+
try {
|
|
1078
|
+
return readFileSync6(resolve6(root, magic, SYNC_CONFIG.VERSION_SENTINEL), "utf-8").trim();
|
|
1079
|
+
} catch {
|
|
1080
|
+
return "";
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
function saveVersionFile(root, magic, version2) {
|
|
1084
|
+
writeFileSync4(resolve6(root, magic, SYNC_CONFIG.VERSION_SENTINEL), version2 + "\n", "utf-8");
|
|
1085
|
+
}
|
|
1086
|
+
function saveHashFile(root, magic, files) {
|
|
1087
|
+
const m = {};
|
|
1088
|
+
for (const [p, c] of files) m[p] = hash8(c);
|
|
1089
|
+
writeFileSync4(resolve6(root, magic, SYNC_CONFIG.HASH_OUTPUT_FILE), JSON.stringify(m, null, 2) + "\n", "utf-8");
|
|
1090
|
+
}
|
|
1013
1091
|
async function syncCommand(options = {}) {
|
|
1014
1092
|
const projectRoot = process.cwd();
|
|
1015
1093
|
const target = resolveAdapter2(projectRoot, options.adapter);
|
|
@@ -1040,6 +1118,75 @@ async function syncCommand(options = {}) {
|
|
|
1040
1118
|
for (const [p, c] of claudeFiles) allFiles.set(p, c);
|
|
1041
1119
|
console.log(`claude adapter (via Agent Host): ${claudeFiles.size} \u6587\u4EF6`);
|
|
1042
1120
|
}
|
|
1121
|
+
if (options.patch) {
|
|
1122
|
+
const candidates = /* @__PURE__ */ new Map();
|
|
1123
|
+
let skipped = 0;
|
|
1124
|
+
for (const [p, c] of allFiles) {
|
|
1125
|
+
if (isUserData(p)) {
|
|
1126
|
+
skipped++;
|
|
1127
|
+
continue;
|
|
1128
|
+
}
|
|
1129
|
+
candidates.set(p, c);
|
|
1130
|
+
}
|
|
1131
|
+
const outHash = loadHashFile(projectRoot, magicDir);
|
|
1132
|
+
const srcHashPath = resolve6(projectRoot, "node_modules", "add-coder", "templates", ".add-coder-src-hash.json");
|
|
1133
|
+
let npmVersion = "";
|
|
1134
|
+
try {
|
|
1135
|
+
npmVersion = JSON.parse(readFileSync6(srcHashPath, "utf-8"))._version ?? "";
|
|
1136
|
+
} catch {
|
|
1137
|
+
}
|
|
1138
|
+
const installedVersion = loadVersionFile(projectRoot, magicDir);
|
|
1139
|
+
const isFirstPatch = !installedVersion;
|
|
1140
|
+
const isUpgrade = installedVersion && npmVersion && installedVersion !== npmVersion;
|
|
1141
|
+
const hashLost = installedVersion && !isUpgrade && Object.keys(outHash).length === 0;
|
|
1142
|
+
const establishBaseline = isFirstPatch || isUpgrade;
|
|
1143
|
+
if (isFirstPatch) {
|
|
1144
|
+
console.log(`\u{1F3AF} \u9996\u6B21 patch\uFF0C\u5EFA\u7ACB\u57FA\u7EBF v${npmVersion}`);
|
|
1145
|
+
} else if (isUpgrade) {
|
|
1146
|
+
console.log(`\u{1F3AF} \u7248\u672C\u5347\u7EA7: ${installedVersion} \u2192 ${npmVersion}\uFF0C\u5EFA\u7ACB\u65B0\u57FA\u7EBF`);
|
|
1147
|
+
} else if (hashLost) {
|
|
1148
|
+
console.log(`\u26A0\uFE0F hash \u4E22\u5931\uFF08\u7248\u672C ${installedVersion} \u672A\u53D8\uFF09\uFF0C\u5168\u90E8\u8FDB\u4EA4\u4E92\u786E\u8BA4`);
|
|
1149
|
+
}
|
|
1150
|
+
const missingFiles = /* @__PURE__ */ new Map();
|
|
1151
|
+
const conflictFiles = /* @__PURE__ */ new Map();
|
|
1152
|
+
let sameCount = 0;
|
|
1153
|
+
for (const [relPath, content] of candidates) {
|
|
1154
|
+
const absPath = resolve6(projectRoot, relPath);
|
|
1155
|
+
if (!existsSync7(absPath)) {
|
|
1156
|
+
missingFiles.set(relPath, content);
|
|
1157
|
+
} else if (establishBaseline) {
|
|
1158
|
+
missingFiles.set(relPath, content);
|
|
1159
|
+
} else {
|
|
1160
|
+
const curH = hash8(readFileSync6(absPath, "utf-8"));
|
|
1161
|
+
const storedH = outHash[relPath];
|
|
1162
|
+
if (storedH && curH === storedH) {
|
|
1163
|
+
sameCount++;
|
|
1164
|
+
} else {
|
|
1165
|
+
conflictFiles.set(relPath, content);
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
console.log(`patch: ${allFiles.size} \u2192 skip user ${skipped} | missing ${missingFiles.size} | conflict ${conflictFiles.size} | same ${sameCount}`);
|
|
1170
|
+
if (missingFiles.size > 0) {
|
|
1171
|
+
const r = await writeFiles2(projectRoot, missingFiles, { force: true, yes: true });
|
|
1172
|
+
console.log(` missing: \u65B0\u5EFA ${r.created}`);
|
|
1173
|
+
}
|
|
1174
|
+
if (conflictFiles.size > 0) {
|
|
1175
|
+
const sel = await selectFiles(projectRoot, conflictFiles);
|
|
1176
|
+
if (sel.size > 0) {
|
|
1177
|
+
const r = await writeFiles2(projectRoot, sel, { force: true, yes: true });
|
|
1178
|
+
console.log(` conflict: \u8986\u76D6 ${r.overwritten}, \u8DF3\u8FC7 ${conflictFiles.size - sel.size}`);
|
|
1179
|
+
} else {
|
|
1180
|
+
console.log(` conflict: \u7528\u6237\u53D6\u6D88\uFF0C${conflictFiles.size} \u4E2A\u6587\u4EF6\u672A\u5199\u5165`);
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
if (missingFiles.size === 0 && conflictFiles.size === 0) {
|
|
1184
|
+
console.log(SYNC_CONFIG.PROMPT_PATCH_DONE);
|
|
1185
|
+
}
|
|
1186
|
+
saveHashFile(projectRoot, magicDir, new Map([...missingFiles, ...conflictFiles]));
|
|
1187
|
+
saveVersionFile(projectRoot, magicDir, npmVersion);
|
|
1188
|
+
return;
|
|
1189
|
+
}
|
|
1043
1190
|
const missing = /* @__PURE__ */ new Map();
|
|
1044
1191
|
for (const [relPath, content] of allFiles) {
|
|
1045
1192
|
if (!existsSync7(resolve6(projectRoot, relPath))) {
|
|
@@ -1047,7 +1194,7 @@ async function syncCommand(options = {}) {
|
|
|
1047
1194
|
}
|
|
1048
1195
|
}
|
|
1049
1196
|
if (missing.size === 0) {
|
|
1050
|
-
console.log(
|
|
1197
|
+
console.log(SYNC_CONFIG.PROMPT_FULL);
|
|
1051
1198
|
return;
|
|
1052
1199
|
}
|
|
1053
1200
|
let filesToWrite = missing;
|
|
@@ -1091,11 +1238,11 @@ async function statusCommand() {
|
|
|
1091
1238
|
|
|
1092
1239
|
// src/cli/index.ts
|
|
1093
1240
|
var { version } = JSON.parse(
|
|
1094
|
-
|
|
1241
|
+
readFileSync7(new URL("../package.json", import.meta.url), "utf-8")
|
|
1095
1242
|
);
|
|
1096
1243
|
var program = new Command();
|
|
1097
1244
|
program.name("add-coder").description("\u521D\u59CB\u5316 ADD \u8303\u5F0F\u5DE5\u4F5C\u6D41\u6A21\u677F").version(version);
|
|
1098
1245
|
program.command("init").description("\u521D\u59CB\u5316 ADD \u6A21\u677F\u5230\u5F53\u524D\u9879\u76EE").option("--adapter <type>", "\u76EE\u6807 IDE: claude | qoder | vscode").option("--config <path>", "\u6307\u5B9A\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").option("--force", "\u8986\u76D6\u5DF2\u6709\u6587\u4EF6\uFF0C\u4E0D\u4EA4\u4E92").option("--dry-run", "\u9884\u89C8\u6A21\u5F0F\uFF0C\u4E0D\u5B9E\u9645\u5199\u5165").action(initCommand);
|
|
1099
|
-
program.command("sync").description("\u589E\u91CF\u540C\u6B65\u7F3A\u5931\u6587\u4EF6").option("--adapter <type>", "\u76EE\u6807 IDE: claude | qoder | vscode").option("-i, --interactive", "\u4EA4\u4E92\u5F0F\u9009\u62E9\u8981\u540C\u6B65\u7684\u6587\u4EF6").action(syncCommand);
|
|
1246
|
+
program.command("sync").description("\u589E\u91CF\u540C\u6B65\u7F3A\u5931\u6587\u4EF6\uFF08--patch \u8986\u76D6\u5DF2\u6709\u6A21\u677F\uFF09").option("--adapter <type>", "\u76EE\u6807 IDE: claude | qoder | vscode").option("--patch", "\u8986\u76D6\u5DF2\u6709\u6A21\u677F\u6587\u4EF6\uFF08\u4E0D\u78B0 plans/specs/reviews\uFF09").option("-i, --interactive", "\u4EA4\u4E92\u5F0F\u9009\u62E9\u8981\u540C\u6B65\u7684\u6587\u4EF6").action(syncCommand);
|
|
1100
1247
|
program.command("status").description("\u68C0\u67E5 ADD \u6A21\u677F\u5B8C\u6574\u6027").action(statusCommand);
|
|
1101
1248
|
program.parse();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "add-coder",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "[codein2027](https://github.com/xiaomingming92/codein2027) - A complete scaffolding for building the ADD programming paradigm, the implementation layer for AI code governance. Core principle: Audit as Infrastructure. Breaks the black-box programming process and cross-session amnesia, evolving programming paradigms into an auditable, traceable, and convergent new era. npx-ready.\n\n[codein2027](https://github.com/xiaomingming92/codein2027) 快速构建 ADD 编程范式的完整脚手架——AI 代码治理的落地方案。以「审计即基础设施」为核心,彻底打破编程过程黑盒与跨轮失忆,让编程范式进化为可审计、可追溯、可收敛的新时代。npx 即用,人人可体验。",
|
|
6
6
|
"repository": {
|
|
@@ -32,25 +32,26 @@
|
|
|
32
32
|
"vscode",
|
|
33
33
|
"mcp",
|
|
34
34
|
"workflow",
|
|
35
|
-
"codein2027"
|
|
35
|
+
"codein2027",
|
|
36
|
+
"裁决层",
|
|
37
|
+
"caijuehub"
|
|
36
38
|
],
|
|
37
39
|
"author": "xiaomingming",
|
|
38
40
|
"email": "wujixmm@gmail.com",
|
|
39
41
|
"license": "AGPL-3.0-only",
|
|
40
42
|
"dependencies": {
|
|
43
|
+
"@modelcontextprotocol/server": "2.0.0-beta.5",
|
|
41
44
|
"commander": "^13.1.0",
|
|
42
45
|
"smol-toml": "^1.7.0",
|
|
43
|
-
"zod": "^
|
|
46
|
+
"zod": "^4.4.3"
|
|
44
47
|
},
|
|
45
48
|
"peerDependencies": {
|
|
46
|
-
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
47
49
|
"@prisma/adapter-libsql": "^7.0.0",
|
|
48
50
|
"@prisma/adapter-pg": "^7.0.0",
|
|
49
51
|
"@prisma/client": "^7.0.0",
|
|
50
52
|
"dotenv": "*",
|
|
51
53
|
"prisma": "^7.0.0",
|
|
52
|
-
"tsx": "
|
|
53
|
-
"zod": "^3.24.0"
|
|
54
|
+
"tsx": ">=4"
|
|
54
55
|
},
|
|
55
56
|
"peerDependenciesMeta": {
|
|
56
57
|
"@prisma/client": {
|
|
@@ -59,12 +60,6 @@
|
|
|
59
60
|
"prisma": {
|
|
60
61
|
"optional": false
|
|
61
62
|
},
|
|
62
|
-
"@modelcontextprotocol/sdk": {
|
|
63
|
-
"optional": false
|
|
64
|
-
},
|
|
65
|
-
"zod": {
|
|
66
|
-
"optional": false
|
|
67
|
-
},
|
|
68
63
|
"dotenv": {
|
|
69
64
|
"optional": false
|
|
70
65
|
},
|
|
@@ -96,6 +91,8 @@
|
|
|
96
91
|
"lint": "eslint src/",
|
|
97
92
|
"lint:fix": "eslint src/ --fix",
|
|
98
93
|
"generate": "tsx src/caijuehub/transcribe.ts",
|
|
94
|
+
"podman-add-coder-up": "podman-compose --env-file .env.development -f podman-compose.add.yml up -d",
|
|
95
|
+
"podman-add-coder-down": "podman-compose --env-file .env.development -f podman-compose.add.yml down",
|
|
99
96
|
"test": "vitest run"
|
|
100
97
|
}
|
|
101
98
|
}
|