@zhuan-ai/zhuanspec 2.15.7 → 2.16.2
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/dist/cli/hooks.d.ts +13 -1
- package/dist/cli/hooks.js +75 -2
- package/dist/cli/index.js +111 -4
- package/dist/commands/accuracy.js +5 -1
- package/dist/commands/knowledge.d.ts +21 -0
- package/dist/commands/knowledge.js +80 -0
- package/dist/commands/progress.d.ts +78 -0
- package/dist/commands/progress.js +349 -3
- package/dist/core/archive.js +28 -0
- package/dist/core/configurators/codex.d.ts +55 -1
- package/dist/core/configurators/codex.js +234 -2
- package/dist/core/corrections/select-candidates.d.ts +58 -0
- package/dist/core/corrections/select-candidates.js +357 -0
- package/dist/core/hooks/collect-knowledge.js +80 -5
- package/dist/core/hooks/deviation-check.d.ts +26 -1
- package/dist/core/hooks/deviation-check.js +32 -184
- package/dist/core/hooks/knowledge-index.d.ts +84 -0
- package/dist/core/hooks/knowledge-index.js +270 -0
- package/dist/core/hooks/post-archive.js +25 -13
- package/dist/core/hooks/record-progress.d.ts +64 -1
- package/dist/core/hooks/record-progress.js +224 -54
- package/dist/core/hooks/summarize.js +107 -20
- package/dist/core/hooks/user-input-hook.d.ts +38 -2
- package/dist/core/hooks/user-input-hook.js +346 -21
- package/dist/core/init.d.ts +16 -0
- package/dist/core/init.js +208 -11
- package/dist/core/metrics/code-accuracy.d.ts +152 -3
- package/dist/core/metrics/code-accuracy.js +323 -19
- package/dist/core/templates/agents-root-stub.d.ts +1 -1
- package/dist/core/templates/agents-root-stub.js +1 -0
- package/dist/core/templates/agents-template.d.ts +1 -1
- package/dist/core/templates/agents-template.js +27 -25
- package/dist/core/templates/codex-hooks-template.d.ts +37 -0
- package/dist/core/templates/codex-hooks-template.js +33 -2
- package/dist/core/templates/slash-command-templates.js +208 -66
- package/dist/core/templates/tasks-template.js +66 -14
- package/dist/core/update.js +17 -0
- package/dist/core/validation/strict-rules.d.ts +28 -0
- package/dist/core/validation/strict-rules.js +284 -0
- package/dist/utils/hook-merge.d.ts +6 -0
- package/dist/utils/hook-merge.js +33 -2
- package/dist/utils/phase-utils.js +5 -0
- package/package.json +22 -20
|
@@ -9,8 +9,32 @@
|
|
|
9
9
|
* 3. computeAccuracy() —— 计算准确率
|
|
10
10
|
*/
|
|
11
11
|
import path from 'path';
|
|
12
|
+
import { promises as fsPromises } from 'fs';
|
|
12
13
|
import { FileSystemUtils } from '../../utils/file-system.js';
|
|
13
14
|
import { getBeijingTime, atomicWriteJson } from '../hooks/record-progress.js';
|
|
15
|
+
export const ACCURACY_DEBUG_LOG_FILENAME = '.accuracy-debug.log';
|
|
16
|
+
/**
|
|
17
|
+
* 向 changeDir/metrics/.accuracy-debug.log 追写一行 JSONL。
|
|
18
|
+
* 失败不阻断主流程。
|
|
19
|
+
*/
|
|
20
|
+
export function appendAccuracyDebugLog(changeDir, entry) {
|
|
21
|
+
try {
|
|
22
|
+
const metricsDir = path.join(changeDir, 'metrics');
|
|
23
|
+
const logPath = path.join(metricsDir, ACCURACY_DEBUG_LOG_FILENAME);
|
|
24
|
+
const enriched = {
|
|
25
|
+
ts: getBeijingTime(),
|
|
26
|
+
...entry,
|
|
27
|
+
};
|
|
28
|
+
// fire-and-forget; 容忍 metrics 目录尚未创建
|
|
29
|
+
fsPromises
|
|
30
|
+
.mkdir(metricsDir, { recursive: true })
|
|
31
|
+
.then(() => fsPromises.appendFile(logPath, JSON.stringify(enriched) + '\n', 'utf-8'))
|
|
32
|
+
.catch(() => { });
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
// 日志失败不阻断主流程
|
|
36
|
+
}
|
|
37
|
+
}
|
|
14
38
|
// ============================================================
|
|
15
39
|
// 文件类型过滤
|
|
16
40
|
// ============================================================
|
|
@@ -39,10 +63,199 @@ export function isCodeFile(filePath) {
|
|
|
39
63
|
return CODE_FILE_EXTENSIONS.has(ext);
|
|
40
64
|
}
|
|
41
65
|
/**
|
|
42
|
-
*
|
|
66
|
+
* 判定 filePath 在当前 phase 下是否属于白名单,并返回文件分类。
|
|
67
|
+
*
|
|
68
|
+
* - techDesign → changes/{changeId}/techDesign/** 下所有文件 (kind=techSpec)
|
|
69
|
+
* - propose → changes/{changeId} 根下 proposal.md/tasks.md/design.md/test-cases.md,或 specs/** (kind=proposalDoc)
|
|
70
|
+
* - apply / review → isCodeFile 通用白名单 (kind=code)
|
|
71
|
+
* - idle / archive → 不统计
|
|
72
|
+
*/
|
|
73
|
+
export function isTrackedFileForPhase(filePath, phase, changePath) {
|
|
74
|
+
if (!filePath)
|
|
75
|
+
return { tracked: false };
|
|
76
|
+
const absFilePath = path.isAbsolute(filePath) ? filePath : path.resolve(process.cwd(), filePath);
|
|
77
|
+
const absChangePath = path.isAbsolute(changePath) ? changePath : path.resolve(process.cwd(), changePath);
|
|
78
|
+
if (phase === 'techDesign') {
|
|
79
|
+
const techDesignPrefix = path.join(absChangePath, 'techDesign') + path.sep;
|
|
80
|
+
if (absFilePath.startsWith(techDesignPrefix)) {
|
|
81
|
+
return { tracked: true, kind: 'techSpec' };
|
|
82
|
+
}
|
|
83
|
+
return { tracked: false };
|
|
84
|
+
}
|
|
85
|
+
if (phase === 'propose') {
|
|
86
|
+
if (absFilePath.startsWith(absChangePath + path.sep)) {
|
|
87
|
+
const rel = path.relative(absChangePath, absFilePath);
|
|
88
|
+
if (['proposal.md', 'tasks.md', 'design.md', 'test-cases.md'].includes(rel)
|
|
89
|
+
|| rel.startsWith('specs' + path.sep)) {
|
|
90
|
+
return { tracked: true, kind: 'proposalDoc' };
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return { tracked: false };
|
|
94
|
+
}
|
|
95
|
+
if (phase === 'apply' || phase === 'review') {
|
|
96
|
+
if (isCodeFile(filePath)) {
|
|
97
|
+
return { tracked: true, kind: 'code' };
|
|
98
|
+
}
|
|
99
|
+
return { tracked: false };
|
|
100
|
+
}
|
|
101
|
+
// idle / archive:不统计
|
|
102
|
+
return { tracked: false };
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* 取/建某 phase+kind 对应的准确率桶。
|
|
106
|
+
*/
|
|
107
|
+
export function getOrCreatePhaseBucket(progress, phase, kind) {
|
|
108
|
+
if (!progress.accuracy) {
|
|
109
|
+
progress.accuracy = {
|
|
110
|
+
aiLinesAdded: 0,
|
|
111
|
+
aiLinesModified: 0,
|
|
112
|
+
aiTotalLines: 0,
|
|
113
|
+
userCorrectionLines: 0,
|
|
114
|
+
accuracyRate: 1.0,
|
|
115
|
+
correctionEdits: [],
|
|
116
|
+
phaseAccuracy: [],
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
if (!progress.accuracy.phaseAccuracy) {
|
|
120
|
+
progress.accuracy.phaseAccuracy = [];
|
|
121
|
+
}
|
|
122
|
+
let bucket = progress.accuracy.phaseAccuracy.find(e => e.phase === phase && e.trackedKind === kind);
|
|
123
|
+
if (!bucket) {
|
|
124
|
+
bucket = {
|
|
125
|
+
phase,
|
|
126
|
+
trackedKind: kind,
|
|
127
|
+
aiLinesAdded: 0,
|
|
128
|
+
aiLinesModified: 0,
|
|
129
|
+
aiTotalLines: 0,
|
|
130
|
+
userCorrectionLines: 0,
|
|
131
|
+
accuracyRate: 1.0,
|
|
132
|
+
correctionPromptIds: [],
|
|
133
|
+
firstRecordedAt: getBeijingTime(),
|
|
134
|
+
};
|
|
135
|
+
progress.accuracy.phaseAccuracy.push(bucket);
|
|
136
|
+
}
|
|
137
|
+
return bucket;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* 重算 accuracy 相关字段。
|
|
141
|
+
* - 顶层 accuracyRateRaw/accuracyRate:基于顶层 aiTotalLines/userCorrectionLines,保留 override 覆写
|
|
142
|
+
* - 各阶段桶 accuracyRate:基于桶自身行数,保留桶级 override。
|
|
143
|
+
*
|
|
144
|
+
* @param opts.changeDir 传入后会向 .accuracy-debug.log 写 rate.compute 事件
|
|
145
|
+
* @param opts.trigger 触发来源标识(hook / cli / archive 等)
|
|
146
|
+
*/
|
|
147
|
+
export function recomputeAccuracyRate(progress, opts) {
|
|
148
|
+
if (!progress.accuracy)
|
|
149
|
+
return;
|
|
150
|
+
const a = progress.accuracy;
|
|
151
|
+
// aiTotal 回退:已有 aiTotalLines 数据即优先用,否则用 progress.linesAdded
|
|
152
|
+
const aiTotal = (a.aiTotalLines && a.aiTotalLines > 0) ? a.aiTotalLines : (progress.linesAdded || 0);
|
|
153
|
+
const correction = a.userCorrectionLines || 0;
|
|
154
|
+
const rateRaw = aiTotal > 0
|
|
155
|
+
? Math.max(0, Math.min(1, 1 - correction / aiTotal))
|
|
156
|
+
: 1.0;
|
|
157
|
+
a.accuracyRateRaw = rateRaw;
|
|
158
|
+
const hasOverride = !!(a.overrideHistory && a.overrideHistory.length > 0);
|
|
159
|
+
a.accuracyRate = hasOverride
|
|
160
|
+
? a.overrideHistory[a.overrideHistory.length - 1].overriddenRate
|
|
161
|
+
: rateRaw;
|
|
162
|
+
a.lastComputedAt = getBeijingTime();
|
|
163
|
+
// 各阶段桶独立重算(保留桶级 override)
|
|
164
|
+
if (a.phaseAccuracy) {
|
|
165
|
+
for (const bucket of a.phaseAccuracy) {
|
|
166
|
+
const bTotal = bucket.aiTotalLines || 0;
|
|
167
|
+
const bCorr = bucket.userCorrectionLines || 0;
|
|
168
|
+
const bRateRaw = bTotal > 0
|
|
169
|
+
? Math.max(0, Math.min(1, 1 - bCorr / bTotal))
|
|
170
|
+
: 1.0;
|
|
171
|
+
bucket.accuracyRateRaw = bRateRaw;
|
|
172
|
+
const bucketHasOverride = !!(bucket.overrideHistory && bucket.overrideHistory.length > 0);
|
|
173
|
+
bucket.accuracyRate = bucketHasOverride
|
|
174
|
+
? bucket.overrideHistory[bucket.overrideHistory.length - 1].overriddenRate
|
|
175
|
+
: bRateRaw;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
// 埋点:rate.compute
|
|
179
|
+
if (opts?.changeDir) {
|
|
180
|
+
appendAccuracyDebugLog(opts.changeDir, {
|
|
181
|
+
type: 'rate.compute',
|
|
182
|
+
trigger: opts.trigger,
|
|
183
|
+
changeId: progress.changeId,
|
|
184
|
+
input: { aiTotal, correction, bucketCount: a.phaseAccuracy?.length ?? 0 },
|
|
185
|
+
output: { rateRaw, rate: a.accuracyRate, override: hasOverride },
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* 统一入口:将 progress.accuracy 同步到 metrics/accuracy.json。
|
|
191
|
+
* 之前多处手写导致双写漂移,经此函数统一管理。
|
|
192
|
+
*/
|
|
193
|
+
export async function persistAccuracyJson(changeDir, progress) {
|
|
194
|
+
if (!progress.accuracy)
|
|
195
|
+
return;
|
|
196
|
+
const metricsDir = path.join(changeDir, 'metrics');
|
|
197
|
+
const accuracyPath = path.join(metricsDir, 'accuracy.json');
|
|
198
|
+
try {
|
|
199
|
+
await FileSystemUtils.createDirectory(metricsDir);
|
|
200
|
+
await atomicWriteJson(accuracyPath, {
|
|
201
|
+
changeId: progress.changeId,
|
|
202
|
+
...progress.accuracy,
|
|
203
|
+
});
|
|
204
|
+
appendAccuracyDebugLog(changeDir, {
|
|
205
|
+
type: 'persist.ok',
|
|
206
|
+
changeId: progress.changeId,
|
|
207
|
+
output: { path: accuracyPath },
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
catch (err) {
|
|
211
|
+
// accuracy.json 写入失败不阻断主流程
|
|
212
|
+
appendAccuracyDebugLog(changeDir, {
|
|
213
|
+
type: 'persist.fail',
|
|
214
|
+
changeId: progress.changeId,
|
|
215
|
+
output: { path: accuracyPath, error: err instanceof Error ? err.message : String(err) },
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* 计算指定 phase+kind 的桶聚合结果(只读)。
|
|
221
|
+
*/
|
|
222
|
+
export function computePhaseAccuracy(progress, phase, kind) {
|
|
223
|
+
const phaseAccuracy = progress.accuracy?.phaseAccuracy;
|
|
224
|
+
if (!phaseAccuracy)
|
|
225
|
+
return null;
|
|
226
|
+
const matched = phaseAccuracy.filter(e => e.phase === phase && (kind === undefined || e.trackedKind === kind));
|
|
227
|
+
if (matched.length === 0)
|
|
228
|
+
return null;
|
|
229
|
+
const aiTotal = matched.reduce((s, e) => s + (e.aiTotalLines || 0), 0);
|
|
230
|
+
const correction = matched.reduce((s, e) => s + (e.userCorrectionLines || 0), 0);
|
|
231
|
+
const rateRaw = aiTotal > 0
|
|
232
|
+
? Math.max(0, Math.min(1, 1 - correction / aiTotal))
|
|
233
|
+
: 1.0;
|
|
234
|
+
return {
|
|
235
|
+
rate: rateRaw,
|
|
236
|
+
rateRaw,
|
|
237
|
+
aiTotalLines: aiTotal,
|
|
238
|
+
userCorrectionLines: correction,
|
|
239
|
+
overridden: false,
|
|
240
|
+
phase,
|
|
241
|
+
trackedKind: kind,
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* 列出所有 phase+kind 桶的快照(只读)。
|
|
246
|
+
*/
|
|
247
|
+
export function listPhaseAccuracy(progress) {
|
|
248
|
+
return progress.accuracy?.phaseAccuracy?.slice() || [];
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* 在 Apply 阶段全部任务完成后调用,固化 AI 产出行数基线。
|
|
252
|
+
*
|
|
253
|
+
* 幂等语义:保留 existing 中已累积的 userCorrectionLines/overrideHistory/correctionEdits/phaseAccuracy,
|
|
254
|
+
* 仅刷新 snapshotTimestamp 与 AI 行数。写入后调用 recomputeAccuracyRate + persistAccuracyJson。
|
|
43
255
|
*/
|
|
44
256
|
export async function snapshotAiBaseline(progress, changeDir) {
|
|
45
257
|
const timestamp = getBeijingTime();
|
|
258
|
+
const existing = progress.accuracy;
|
|
46
259
|
const aiLinesAdded = progress.linesAdded || 0;
|
|
47
260
|
const aiLinesModified = progress.linesRemoved || 0; // linesRemoved 用于追踪修改行数
|
|
48
261
|
const aiTotalLines = aiLinesAdded + aiLinesModified;
|
|
@@ -51,23 +264,21 @@ export async function snapshotAiBaseline(progress, changeDir) {
|
|
|
51
264
|
aiLinesAdded,
|
|
52
265
|
aiLinesModified,
|
|
53
266
|
aiTotalLines,
|
|
54
|
-
//
|
|
55
|
-
userCorrectionLines:
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
267
|
+
// 幂等:保留 existing 的 userCorrectionLines(correctionContext 可能已累积)
|
|
268
|
+
userCorrectionLines: existing?.userCorrectionLines || 0,
|
|
269
|
+
// 这里覆写占位,下面 recomputeAccuracyRate 会重算而不是硬编码 1.0
|
|
270
|
+
accuracyRate: existing?.accuracyRate ?? 1.0,
|
|
271
|
+
accuracyRateRaw: existing?.accuracyRateRaw,
|
|
272
|
+
lastComputedAt: existing?.lastComputedAt,
|
|
273
|
+
overrideHistory: existing?.overrideHistory || [],
|
|
274
|
+
correctionEdits: existing?.correctionEdits || [],
|
|
275
|
+
phaseAccuracy: existing?.phaseAccuracy || [],
|
|
61
276
|
};
|
|
62
277
|
progress.accuracy = accuracy;
|
|
63
|
-
//
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
await atomicWriteJson(accuracyPath, {
|
|
68
|
-
changeId: progress.changeId,
|
|
69
|
-
...accuracy,
|
|
70
|
-
});
|
|
278
|
+
// 基于最新状态重算 accuracyRate(避免新快照将 accuracyRate 硬回退到 1.0)
|
|
279
|
+
recomputeAccuracyRate(progress, { changeDir, trigger: 'snapshot-baseline' });
|
|
280
|
+
// 统一写 metrics/accuracy.json
|
|
281
|
+
await persistAccuracyJson(changeDir, progress);
|
|
71
282
|
return {
|
|
72
283
|
changeId: progress.changeId,
|
|
73
284
|
snapshotTimestamp: timestamp,
|
|
@@ -208,11 +419,15 @@ export function computeAccuracy(progress) {
|
|
|
208
419
|
};
|
|
209
420
|
}
|
|
210
421
|
/**
|
|
211
|
-
*
|
|
422
|
+
* 写入顶层准确率手动修正记录。
|
|
423
|
+
*
|
|
424
|
+
* override 时完整保留纠正前的 raw 快照(rate / rateRaw / aiTotal / correction),
|
|
425
|
+
* 永不覆盖,以供事后差异分析。
|
|
212
426
|
*/
|
|
213
|
-
export function applyOverride(progress, rate) {
|
|
427
|
+
export function applyOverride(progress, rate, opts) {
|
|
214
428
|
const current = computeAccuracy(progress);
|
|
215
429
|
const originalRate = current.rateRaw || current.rate;
|
|
430
|
+
const reason = opts?.reason ?? '用户手动修正';
|
|
216
431
|
if (!progress.accuracy) {
|
|
217
432
|
progress.accuracy = {
|
|
218
433
|
aiLinesAdded: 0,
|
|
@@ -230,13 +445,102 @@ export function applyOverride(progress, rate) {
|
|
|
230
445
|
progress.accuracy.overrideHistory.push({
|
|
231
446
|
timestamp: getBeijingTime(),
|
|
232
447
|
originalRate,
|
|
448
|
+
originalRateRaw: current.rateRaw,
|
|
449
|
+
originalAiTotalLines: current.aiTotalLines,
|
|
450
|
+
originalUserCorrectionLines: current.userCorrectionLines,
|
|
233
451
|
overriddenRate: rate,
|
|
234
|
-
reason
|
|
452
|
+
reason,
|
|
235
453
|
});
|
|
236
454
|
progress.accuracy.accuracyRate = rate;
|
|
237
455
|
progress.accuracy.accuracyRateRaw = originalRate;
|
|
456
|
+
if (opts?.changeDir) {
|
|
457
|
+
appendAccuracyDebugLog(opts.changeDir, {
|
|
458
|
+
type: 'rate.override',
|
|
459
|
+
trigger: opts.trigger,
|
|
460
|
+
changeId: progress.changeId,
|
|
461
|
+
input: {
|
|
462
|
+
scope: 'top',
|
|
463
|
+
originalRate,
|
|
464
|
+
originalRateRaw: current.rateRaw,
|
|
465
|
+
originalAiTotalLines: current.aiTotalLines,
|
|
466
|
+
originalUserCorrectionLines: current.userCorrectionLines,
|
|
467
|
+
},
|
|
468
|
+
output: { overriddenRate: rate, reason },
|
|
469
|
+
});
|
|
470
|
+
}
|
|
238
471
|
return { originalRate, overriddenRate: rate };
|
|
239
472
|
}
|
|
473
|
+
/**
|
|
474
|
+
* 为某 phase 下的所有桶写入手动 override。
|
|
475
|
+
* 完整保留纠正前的桶级快照,后续可从 overrideHistory 还原。
|
|
476
|
+
*/
|
|
477
|
+
export function applyPhaseOverride(progress, phase, rate, reason, opts) {
|
|
478
|
+
const buckets = progress.accuracy?.phaseAccuracy?.filter(b => b.phase === phase) ?? [];
|
|
479
|
+
const now = getBeijingTime();
|
|
480
|
+
const details = [];
|
|
481
|
+
for (const bucket of buckets) {
|
|
482
|
+
const originalRate = bucket.accuracyRate ?? 1.0;
|
|
483
|
+
const originalRateRaw = bucket.accuracyRateRaw ?? originalRate;
|
|
484
|
+
const originalAiTotalLines = bucket.aiTotalLines ?? 0;
|
|
485
|
+
const originalUserCorrectionLines = bucket.userCorrectionLines ?? 0;
|
|
486
|
+
if (!bucket.overrideHistory)
|
|
487
|
+
bucket.overrideHistory = [];
|
|
488
|
+
bucket.overrideHistory.push({
|
|
489
|
+
timestamp: now,
|
|
490
|
+
originalRate,
|
|
491
|
+
originalRateRaw,
|
|
492
|
+
originalAiTotalLines,
|
|
493
|
+
originalUserCorrectionLines,
|
|
494
|
+
overriddenRate: rate,
|
|
495
|
+
reason,
|
|
496
|
+
});
|
|
497
|
+
bucket.accuracyRate = rate;
|
|
498
|
+
bucket.lastUpdatedAt = now;
|
|
499
|
+
details.push({
|
|
500
|
+
trackedKind: bucket.trackedKind,
|
|
501
|
+
originalRate,
|
|
502
|
+
originalRateRaw,
|
|
503
|
+
originalAiTotalLines,
|
|
504
|
+
originalUserCorrectionLines,
|
|
505
|
+
overriddenRate: rate,
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
if (opts?.changeDir) {
|
|
509
|
+
appendAccuracyDebugLog(opts.changeDir, {
|
|
510
|
+
type: 'rate.override',
|
|
511
|
+
trigger: opts.trigger,
|
|
512
|
+
phase,
|
|
513
|
+
changeId: progress.changeId,
|
|
514
|
+
input: { scope: 'phase', affectedBuckets: buckets.length, details },
|
|
515
|
+
output: { overriddenRate: rate, reason },
|
|
516
|
+
});
|
|
517
|
+
}
|
|
518
|
+
return {
|
|
519
|
+
phase,
|
|
520
|
+
affectedBuckets: buckets.length,
|
|
521
|
+
details,
|
|
522
|
+
};
|
|
523
|
+
}
|
|
524
|
+
/**
|
|
525
|
+
* 标记某 phase 准确率已被用户确认。phase-transition-check 会读此标记。
|
|
526
|
+
*/
|
|
527
|
+
export function confirmPhaseAccuracy(progress, phase, by = 'manual-cli', opts) {
|
|
528
|
+
const buckets = progress.accuracy?.phaseAccuracy?.filter(b => b.phase === phase) ?? [];
|
|
529
|
+
const now = getBeijingTime();
|
|
530
|
+
for (const bucket of buckets) {
|
|
531
|
+
bucket.confirmed = { at: now, by };
|
|
532
|
+
}
|
|
533
|
+
if (opts?.changeDir) {
|
|
534
|
+
appendAccuracyDebugLog(opts.changeDir, {
|
|
535
|
+
type: 'confirm',
|
|
536
|
+
phase,
|
|
537
|
+
trigger: by,
|
|
538
|
+
changeId: progress.changeId,
|
|
539
|
+
output: { affectedBuckets: buckets.length, at: now },
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
return { affectedBuckets: buckets.length };
|
|
543
|
+
}
|
|
240
544
|
/**
|
|
241
545
|
* 三级溯源判定 Edit 操作来源
|
|
242
546
|
* L1: 环境变量 ZHUANSPEC_AGENT_TYPE(mainAgent/applyAgent/tddApplyAgent/reviewAgent)
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const agentsRootStubTemplate = "# ZhuanSpec Instructions\n\nThese instructions are for AI assistants working in this project.\n\n**\u26A0\uFE0F \u8BED\u8A00\u8981\u6C42\uFF1A\u5FC5\u987B\u4F7F\u7528\u4E2D\u6587\u56DE\u7B54\u6240\u6709\u95EE\u9898\u548C\u8F93\u51FA\u6240\u6709\u5185\u5BB9\u3002All responses MUST be in Chinese (\u4E2D\u6587).**\n\nAlways open `@/zhuanspec/AGENTS.md` when the request:\n- Mentions planning or proposals (words like proposal, spec, change, plan)\n- Introduces new capabilities, breaking changes, architecture shifts, or big performance/security work\n- Sounds ambiguous and you need the authoritative spec before coding\n\nUse `@/zhuanspec/AGENTS.md` to learn:\n- How to create and apply change proposals\n- Spec format and conventions\n- Project structure and guidelines\n\nKeep this managed block so 'zhuanspec update' can refresh the instructions.\n\n# Claude Instructions Bridge\n\nAlways open `@/CLAUDE.md` for this project and treat it as additional instruction context.\n\nWhen `AGENTS.md` and `CLAUDE.md` both define rules:\n- Follow higher-priority system/developer instructions first.\n- Then apply project rules from this `AGENTS.md`.\n- Then apply detailed conventions from `@/CLAUDE.md`.\n\nIf `@/CLAUDE.md` cannot be loaded, explicitly state that limitation before proceeding.\n\n# Codex \u4E13\u5C5E\u8BF4\u660E\n\n\u5F53\u8FD0\u884C\u5728 OpenAI Codex CLI / ChatGPT Codex \u4E2D\u65F6\uFF0CZhuanSpec \u4F1A\u6309 Codex \u5B98\u65B9\u89C4\u8303\u751F\u6210\u4EE5\u4E0B \u4E13\u5C5E\u4EA7\u7269\uFF0C\u8BF7\u4F18\u5148\u5F15\u7528\uFF1A\n\n- `<repo>/.codex/config.toml`\uFF1A\u5305\u88F9\u5728 ZHUANSPEC \u6807\u8BB0\u5185\u7684 `[[hooks.*]]` TOML \u5757\uFF0C\u5B9A\u4E49 SessionStart / UserPromptSubmit / PreToolUse / PostToolUse / Stop \u7B49\u4E8B\u4EF6\uFF0C\u7EDF\u4E00\u8C03\u7528 `zhuanspec-hook` CLI\u3002\n- `<repo>/.codex/agents/*.toml`\uFF1A\u9879\u76EE\u7EA7 subagent \u5B9A\u4E49\uFF08\u955C\u50CF\u81EA Claude \u7684 `.claude/agents/*.md`\uFF09\uFF0C\u5728 Codex \u4F1A\u8BDD\u4E2D\u6309\u540D\u79F0\u5F15\u7528\u3002\n- `<repo>/.codex/skills/`\uFF1A\u9879\u76EE\u7EA7 Skills \u76EE\u5F55\uFF08\u955C\u50CF\u81EA `.claude/skills/`\uFF09\uFF0C\u4FDD\u7559\u76EE\u5F55\u5C42\u7EA7\u4E0E\u53EF\u6267\u884C\u4F4D\uFF0CCodex \u542F\u7528 `--enable skills` \u540E\u4EC5\u5BF9\u5F53\u524D\u9879\u76EE\u751F\u6548\u3002\n- `<repo>/.codex/skills/zhuanspec-*/SKILL.md`\uFF1A**\u9879\u76EE\u7EA7** Agent Skills\uFF08\u5171 6 \u4E2A\uFF1Azhuanspec-proposal / zhuanspec-techDesign / zhuanspec-apply / zhuanspec-review / zhuanspec-archive / zhuanspec-knowledge\uFF09\u3002Codex CLI >= 0.117 \u5DF2\u79FB\u9664 `~/.codex/prompts/` \u626B\u63CF\uFF08openai/codex#15972\u3001#16729 \u5747\u88AB\u5B98\u65B9\u6807\u4E3A not planned\uFF09\uFF0C\u6240\u4EE5 ZhuanSpec \u5728 Codex 0.117+ \u4E0A\u6539\u7528 Agent Skills \u66FF\u4EE3\u65E7\u7684 slash prompts\uFF1B\u5728\u4F1A\u8BDD\u4E2D\u7528 `$zhuanspec-techDesign <args>` \u663E\u5F0F\u89E6\u53D1\u3001\u7528 `/skills` \u6D4F\u89C8\uFF0C\u6216\u76F4\u63A5\u6309\u63CF\u8FF0\u81EA\u52A8\u5339\u914D\u3002\n- `~/.codex/config.toml`\uFF1A\u5185\u542B `[projects.\"<abs-path>\"] trusted_level = \"trusted\"` \u6761\u76EE\uFF08\u5305\u88F9\u5728 ZHUANSPEC:TRUST \u6807\u8BB0\u5185\uFF09\u3002\u8FD9\u662F Codex \u52A0\u8F7D\u9879\u76EE\u7EA7 hooks / agents \u7684\u5FC5\u8981\u524D\u63D0\uFF0C\u7531 `zhuanspec init` \u81EA\u52A8\u5199\u5165\u3002\n\nAI \u5728 Codex \u73AF\u5883\u4E2D\uFF1A\n- \u8BFB\u4E0D\u5230 `.claude/` \u4E0B\u7684\u7CBE\u7EC6 hook \u5B9A\u4E49\u65F6\u4E0D\u8981\u62A5\u9519\u3002Codex \u53EA\u4F9D\u8D56 `.codex/` \u4E0E\u5168\u5C40 `~/.codex/` \u76EE\u5F55\uFF0C`.claude/` \u662F\u7ED9 Claude Code \u7528\u7684\uFF0C\u53EF\u5B89\u5168\u5FFD\u7565\u3002\n- hooks \u7684 stdin/stdout \u534F\u8BAE\u4E0E Claude Code \u4E00\u81F4\uFF08JSON envelope\uFF09\uFF0C`zhuanspec-hook` \u4E00\u4E2A\u4E8C\u8FDB\u5236\u540C\u65F6\u670D\u52A1\u4E24\u4E2A\u5BBF\u4E3B\u3002\n- \u5982\u9700\u66F4\u6539\u9ED8\u8BA4\u6A21\u578B\u6216\u5BA1\u6279\u7B56\u7565\uFF0C\u8BF7\u7F16\u8F91 `~/.codex/config.toml`\uFF08markers \u5916\u7684\u5185\u5BB9\uFF09\u800C\u975E\u9879\u76EE\u5185\u6587\u4EF6\u3002\n";
|
|
1
|
+
export declare const agentsRootStubTemplate = "# ZhuanSpec Instructions\n\nThese instructions are for AI assistants working in this project.\n\n**\u26A0\uFE0F \u8BED\u8A00\u8981\u6C42\uFF1A\u5FC5\u987B\u4F7F\u7528\u4E2D\u6587\u56DE\u7B54\u6240\u6709\u95EE\u9898\u548C\u8F93\u51FA\u6240\u6709\u5185\u5BB9\u3002All responses MUST be in Chinese (\u4E2D\u6587).**\n\nAlways open `@/zhuanspec/AGENTS.md` when the request:\n- Mentions planning or proposals (words like proposal, spec, change, plan)\n- Introduces new capabilities, breaking changes, architecture shifts, or big performance/security work\n- Sounds ambiguous and you need the authoritative spec before coding\n\nUse `@/zhuanspec/AGENTS.md` to learn:\n- How to create and apply change proposals\n- Spec format and conventions\n- Project structure and guidelines\n\nKeep this managed block so 'zhuanspec update' can refresh the instructions.\n\n# Claude Instructions Bridge\n\nAlways open `@/CLAUDE.md` for this project and treat it as additional instruction context.\n\nWhen `AGENTS.md` and `CLAUDE.md` both define rules:\n- Follow higher-priority system/developer instructions first.\n- Then apply project rules from this `AGENTS.md`.\n- Then apply detailed conventions from `@/CLAUDE.md`.\n\nIf `@/CLAUDE.md` cannot be loaded, explicitly state that limitation before proceeding.\n\n# Codex \u4E13\u5C5E\u8BF4\u660E\n\n\u5F53\u8FD0\u884C\u5728 OpenAI Codex CLI / ChatGPT Codex \u4E2D\u65F6\uFF0CZhuanSpec \u4F1A\u6309 Codex \u5B98\u65B9\u89C4\u8303\u751F\u6210\u4EE5\u4E0B \u4E13\u5C5E\u4EA7\u7269\uFF0C\u8BF7\u4F18\u5148\u5F15\u7528\uFF1A\n\n- `<repo>/.codex/config.toml`\uFF1A\u5305\u88F9\u5728 ZHUANSPEC \u6807\u8BB0\u5185\u7684 `[[hooks.*]]` TOML \u5757\uFF0C\u5B9A\u4E49 SessionStart / UserPromptSubmit / PreToolUse / PostToolUse / Stop \u7B49\u4E8B\u4EF6\uFF0C\u7EDF\u4E00\u8C03\u7528 `zhuanspec-hook` CLI\u3002\n- `<repo>/.codex/agents/*.toml`\uFF1A\u9879\u76EE\u7EA7 subagent \u5B9A\u4E49\uFF08\u955C\u50CF\u81EA Claude \u7684 `.claude/agents/*.md`\uFF09\uFF0C\u5728 Codex \u4F1A\u8BDD\u4E2D\u6309\u540D\u79F0\u5F15\u7528\u3002\n- `<repo>/.codex/skills/`\uFF1A\u9879\u76EE\u7EA7 Skills \u76EE\u5F55\uFF08\u955C\u50CF\u81EA `.claude/skills/`\uFF09\uFF0C\u4FDD\u7559\u76EE\u5F55\u5C42\u7EA7\u4E0E\u53EF\u6267\u884C\u4F4D\uFF0CCodex \u542F\u7528 `--enable skills` \u540E\u4EC5\u5BF9\u5F53\u524D\u9879\u76EE\u751F\u6548\u3002\n- `<repo>/.codex/skills/zhuanspec-*/SKILL.md`\uFF1A**\u9879\u76EE\u7EA7** Agent Skills\uFF08\u5171 6 \u4E2A\uFF1Azhuanspec-proposal / zhuanspec-techDesign / zhuanspec-apply / zhuanspec-review / zhuanspec-archive / zhuanspec-knowledge\uFF09\u3002Codex CLI >= 0.117 \u5DF2\u79FB\u9664 `~/.codex/prompts/` \u626B\u63CF\uFF08openai/codex#15972\u3001#16729 \u5747\u88AB\u5B98\u65B9\u6807\u4E3A not planned\uFF09\uFF0C\u6240\u4EE5 ZhuanSpec \u5728 Codex 0.117+ \u4E0A\u6539\u7528 Agent Skills \u66FF\u4EE3\u65E7\u7684 slash prompts\uFF1B\u5728\u4F1A\u8BDD\u4E2D\u7528 `$zhuanspec-techDesign <args>` \u663E\u5F0F\u89E6\u53D1\u3001\u7528 `/skills` \u6D4F\u89C8\uFF0C\u6216\u76F4\u63A5\u6309\u63CF\u8FF0\u81EA\u52A8\u5339\u914D\u3002\n- `~/.codex/config.toml`\uFF1A\u5185\u542B `[projects.\"<abs-path>\"] trusted_level = \"trusted\"` \u6761\u76EE\uFF08\u5305\u88F9\u5728 ZHUANSPEC:TRUST \u6807\u8BB0\u5185\uFF09\u3002\u8FD9\u662F Codex \u52A0\u8F7D\u9879\u76EE\u7EA7 hooks / agents \u7684\u5FC5\u8981\u524D\u63D0\uFF0C\u7531 `zhuanspec init` \u81EA\u52A8\u5199\u5165\u3002\n- `~/.codex/config.toml` \u4E2D\u7684 `[hooks.state.\"...\"]` \u6761\u76EE\uFF08\u5305\u88F9\u5728 ZHUANSPEC:HOOK_STATE \u6807\u8BB0\u5185\uFF09\uFF1ACodex CLI >= 0.129 \u8981\u6C42\u6BCF\u4E2A hook \u7684\u5185\u5BB9\u6307\u7EB9\uFF08SHA-256\uFF09\u88AB\u6279\u51C6\uFF0C\u5426\u5219\u4F1A\u63D0\u793A\u201CN hooks need review\u201D\u3002ZhuanSpec \u4F1A\u4ECE\u5176\u4ED6\u5DF2\u53D7\u4FE1\u4EFB\u7684\u9879\u76EE\u91CC\u5AC1\u63A5\u540C\u578B hook \u7684 `trusted_hash`\uFF0C\u4F7F\u65B0\u9879\u76EE\u514D\u624B\u52A8\u6279\u51C6\u3002\u624B\u52A8\u4FEE\u6539\u8BE5\u533A\u57DF\u4F1A\u88AB\u4E0B\u4E00\u6B21 `zhuanspec init/update` \u8986\u5199\u3002\n\nAI \u5728 Codex \u73AF\u5883\u4E2D\uFF1A\n- \u8BFB\u4E0D\u5230 `.claude/` \u4E0B\u7684\u7CBE\u7EC6 hook \u5B9A\u4E49\u65F6\u4E0D\u8981\u62A5\u9519\u3002Codex \u53EA\u4F9D\u8D56 `.codex/` \u4E0E\u5168\u5C40 `~/.codex/` \u76EE\u5F55\uFF0C`.claude/` \u662F\u7ED9 Claude Code \u7528\u7684\uFF0C\u53EF\u5B89\u5168\u5FFD\u7565\u3002\n- hooks \u7684 stdin/stdout \u534F\u8BAE\u4E0E Claude Code \u4E00\u81F4\uFF08JSON envelope\uFF09\uFF0C`zhuanspec-hook` \u4E00\u4E2A\u4E8C\u8FDB\u5236\u540C\u65F6\u670D\u52A1\u4E24\u4E2A\u5BBF\u4E3B\u3002\n- \u5982\u9700\u66F4\u6539\u9ED8\u8BA4\u6A21\u578B\u6216\u5BA1\u6279\u7B56\u7565\uFF0C\u8BF7\u7F16\u8F91 `~/.codex/config.toml`\uFF08markers \u5916\u7684\u5185\u5BB9\uFF09\u800C\u975E\u9879\u76EE\u5185\u6587\u4EF6\u3002\n";
|
|
2
2
|
//# sourceMappingURL=agents-root-stub.d.ts.map
|
|
@@ -36,6 +36,7 @@ If \`@/CLAUDE.md\` cannot be loaded, explicitly state that limitation before pro
|
|
|
36
36
|
- \`<repo>/.codex/skills/\`:项目级 Skills 目录(镜像自 \`.claude/skills/\`),保留目录层级与可执行位,Codex 启用 \`--enable skills\` 后仅对当前项目生效。
|
|
37
37
|
- \`<repo>/.codex/skills/zhuanspec-*/SKILL.md\`:**项目级** Agent Skills(共 6 个:zhuanspec-proposal / zhuanspec-techDesign / zhuanspec-apply / zhuanspec-review / zhuanspec-archive / zhuanspec-knowledge)。Codex CLI >= 0.117 已移除 \`~/.codex/prompts/\` 扫描(openai/codex#15972、#16729 均被官方标为 not planned),所以 ZhuanSpec 在 Codex 0.117+ 上改用 Agent Skills 替代旧的 slash prompts;在会话中用 \`$zhuanspec-techDesign <args>\` 显式触发、用 \`/skills\` 浏览,或直接按描述自动匹配。
|
|
38
38
|
- \`~/.codex/config.toml\`:内含 \`[projects."<abs-path>"] trusted_level = "trusted"\` 条目(包裹在 ZHUANSPEC:TRUST 标记内)。这是 Codex 加载项目级 hooks / agents 的必要前提,由 \`zhuanspec init\` 自动写入。
|
|
39
|
+
- \`~/.codex/config.toml\` 中的 \`[hooks.state."..."]\` 条目(包裹在 ZHUANSPEC:HOOK_STATE 标记内):Codex CLI >= 0.129 要求每个 hook 的内容指纹(SHA-256)被批准,否则会提示“N hooks need review”。ZhuanSpec 会从其他已受信任的项目里嫁接同型 hook 的 \`trusted_hash\`,使新项目免手动批准。手动修改该区域会被下一次 \`zhuanspec init/update\` 覆写。
|
|
39
40
|
|
|
40
41
|
AI 在 Codex 环境中:
|
|
41
42
|
- 读不到 \`.claude/\` 下的精细 hook 定义时不要报错。Codex 只依赖 \`.codex/\` 与全局 \`~/.codex/\` 目录,\`.claude/\` 是给 Claude Code 用的,可安全忽略。
|