@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.
Files changed (43) hide show
  1. package/dist/cli/hooks.d.ts +13 -1
  2. package/dist/cli/hooks.js +75 -2
  3. package/dist/cli/index.js +111 -4
  4. package/dist/commands/accuracy.js +5 -1
  5. package/dist/commands/knowledge.d.ts +21 -0
  6. package/dist/commands/knowledge.js +80 -0
  7. package/dist/commands/progress.d.ts +78 -0
  8. package/dist/commands/progress.js +349 -3
  9. package/dist/core/archive.js +28 -0
  10. package/dist/core/configurators/codex.d.ts +55 -1
  11. package/dist/core/configurators/codex.js +234 -2
  12. package/dist/core/corrections/select-candidates.d.ts +58 -0
  13. package/dist/core/corrections/select-candidates.js +357 -0
  14. package/dist/core/hooks/collect-knowledge.js +80 -5
  15. package/dist/core/hooks/deviation-check.d.ts +26 -1
  16. package/dist/core/hooks/deviation-check.js +32 -184
  17. package/dist/core/hooks/knowledge-index.d.ts +84 -0
  18. package/dist/core/hooks/knowledge-index.js +270 -0
  19. package/dist/core/hooks/post-archive.js +25 -13
  20. package/dist/core/hooks/record-progress.d.ts +64 -1
  21. package/dist/core/hooks/record-progress.js +224 -54
  22. package/dist/core/hooks/summarize.js +107 -20
  23. package/dist/core/hooks/user-input-hook.d.ts +38 -2
  24. package/dist/core/hooks/user-input-hook.js +346 -21
  25. package/dist/core/init.d.ts +16 -0
  26. package/dist/core/init.js +208 -11
  27. package/dist/core/metrics/code-accuracy.d.ts +152 -3
  28. package/dist/core/metrics/code-accuracy.js +323 -19
  29. package/dist/core/templates/agents-root-stub.d.ts +1 -1
  30. package/dist/core/templates/agents-root-stub.js +1 -0
  31. package/dist/core/templates/agents-template.d.ts +1 -1
  32. package/dist/core/templates/agents-template.js +27 -25
  33. package/dist/core/templates/codex-hooks-template.d.ts +37 -0
  34. package/dist/core/templates/codex-hooks-template.js +33 -2
  35. package/dist/core/templates/slash-command-templates.js +208 -66
  36. package/dist/core/templates/tasks-template.js +66 -14
  37. package/dist/core/update.js +17 -0
  38. package/dist/core/validation/strict-rules.d.ts +28 -0
  39. package/dist/core/validation/strict-rules.js +284 -0
  40. package/dist/utils/hook-merge.d.ts +6 -0
  41. package/dist/utils/hook-merge.js +33 -2
  42. package/dist/utils/phase-utils.js +5 -0
  43. 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
- * 在 Apply 阶段全部任务完成后调用,固化 AI 产出行数基线
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
- // 保留已有纠正数据(correctionContext 机制可能在快照前已累积),而非硬编码为 0
55
- userCorrectionLines: progress.accuracy?.userCorrectionLines || 0,
56
- accuracyRate: 1.0,
57
- accuracyRateRaw: undefined,
58
- lastComputedAt: undefined,
59
- overrideHistory: progress.accuracy?.overrideHistory || [],
60
- correctionEdits: progress.accuracy?.correctionEdits || [],
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
- // 写入独立的 accuracy.json
64
- const metricsDir = path.join(changeDir, 'metrics');
65
- await FileSystemUtils.createDirectory(metricsDir);
66
- const accuracyPath = path.join(metricsDir, 'accuracy.json');
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 用的,可安全忽略。