minimal-vibe-coding-kit 0.5.0 → 0.5.1

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 (91) hide show
  1. package/.agents/skills/tutien/SKILL.md +36 -13
  2. package/.agents/skills/tutien/references/adaptive-response.md +68 -0
  3. package/.agents/skills/tutien/references/lore-sources.md +28 -0
  4. package/.agents/skills/tutien/references/privacy.md +13 -1
  5. package/.agents/skills/tutien/references/schema.md +32 -1
  6. package/.agents/skills/tutien/references/story-system.md +176 -0
  7. package/.agents/skills/tutien/references/vi-style-guide.md +89 -0
  8. package/.agents/skills/tutien/references/voice-and-mode.md +23 -8
  9. package/.agents/skills/tutien/scripts/catalog.mjs +17 -17
  10. package/.agents/skills/tutien/scripts/classify.mjs +64 -37
  11. package/.agents/skills/tutien/scripts/command.mjs +40 -21
  12. package/.agents/skills/tutien/scripts/project-profile.mjs +202 -0
  13. package/.agents/skills/tutien/scripts/render-report.mjs +128 -72
  14. package/.agents/skills/tutien/scripts/response-brief.mjs +103 -0
  15. package/.agents/skills/tutien/scripts/run-tutien.mjs +141 -63
  16. package/.agents/skills/tutien/scripts/story-ledger.mjs +241 -0
  17. package/.agents/skills/tutien/scripts/villains.mjs +14 -14
  18. package/.claude/skills/tutien/SKILL.md +36 -13
  19. package/.claude/skills/tutien/references/adaptive-response.md +68 -0
  20. package/.claude/skills/tutien/references/lore-sources.md +28 -0
  21. package/.claude/skills/tutien/references/privacy.md +13 -1
  22. package/.claude/skills/tutien/references/schema.md +32 -1
  23. package/.claude/skills/tutien/references/story-system.md +176 -0
  24. package/.claude/skills/tutien/references/vi-style-guide.md +89 -0
  25. package/.claude/skills/tutien/references/voice-and-mode.md +23 -8
  26. package/.claude/skills/tutien/scripts/catalog.mjs +17 -17
  27. package/.claude/skills/tutien/scripts/classify.mjs +64 -37
  28. package/.claude/skills/tutien/scripts/command.mjs +40 -21
  29. package/.claude/skills/tutien/scripts/project-profile.mjs +202 -0
  30. package/.claude/skills/tutien/scripts/render-report.mjs +128 -72
  31. package/.claude/skills/tutien/scripts/response-brief.mjs +103 -0
  32. package/.claude/skills/tutien/scripts/run-tutien.mjs +141 -63
  33. package/.claude/skills/tutien/scripts/story-ledger.mjs +241 -0
  34. package/.claude/skills/tutien/scripts/villains.mjs +14 -14
  35. package/.codex-plugin/plugin.json +1 -1
  36. package/.cursor/skills/tutien/SKILL.md +36 -13
  37. package/.cursor/skills/tutien/references/adaptive-response.md +68 -0
  38. package/.cursor/skills/tutien/references/lore-sources.md +28 -0
  39. package/.cursor/skills/tutien/references/privacy.md +13 -1
  40. package/.cursor/skills/tutien/references/schema.md +32 -1
  41. package/.cursor/skills/tutien/references/story-system.md +176 -0
  42. package/.cursor/skills/tutien/references/vi-style-guide.md +89 -0
  43. package/.cursor/skills/tutien/references/voice-and-mode.md +23 -8
  44. package/.cursor/skills/tutien/scripts/catalog.mjs +17 -17
  45. package/.cursor/skills/tutien/scripts/classify.mjs +64 -37
  46. package/.cursor/skills/tutien/scripts/command.mjs +40 -21
  47. package/.cursor/skills/tutien/scripts/project-profile.mjs +202 -0
  48. package/.cursor/skills/tutien/scripts/render-report.mjs +128 -72
  49. package/.cursor/skills/tutien/scripts/response-brief.mjs +103 -0
  50. package/.cursor/skills/tutien/scripts/run-tutien.mjs +141 -63
  51. package/.cursor/skills/tutien/scripts/story-ledger.mjs +241 -0
  52. package/.cursor/skills/tutien/scripts/villains.mjs +14 -14
  53. package/.grok/skills/tutien/SKILL.md +36 -13
  54. package/.grok/skills/tutien/references/adaptive-response.md +68 -0
  55. package/.grok/skills/tutien/references/lore-sources.md +28 -0
  56. package/.grok/skills/tutien/references/privacy.md +13 -1
  57. package/.grok/skills/tutien/references/schema.md +32 -1
  58. package/.grok/skills/tutien/references/story-system.md +176 -0
  59. package/.grok/skills/tutien/references/vi-style-guide.md +89 -0
  60. package/.grok/skills/tutien/references/voice-and-mode.md +23 -8
  61. package/.grok/skills/tutien/scripts/catalog.mjs +17 -17
  62. package/.grok/skills/tutien/scripts/classify.mjs +64 -37
  63. package/.grok/skills/tutien/scripts/command.mjs +40 -21
  64. package/.grok/skills/tutien/scripts/project-profile.mjs +202 -0
  65. package/.grok/skills/tutien/scripts/render-report.mjs +128 -72
  66. package/.grok/skills/tutien/scripts/response-brief.mjs +103 -0
  67. package/.grok/skills/tutien/scripts/run-tutien.mjs +141 -63
  68. package/.grok/skills/tutien/scripts/story-ledger.mjs +241 -0
  69. package/.grok/skills/tutien/scripts/villains.mjs +14 -14
  70. package/.vibekit/skills/tutien/SKILL.md +36 -13
  71. package/.vibekit/skills/tutien/references/adaptive-response.md +68 -0
  72. package/.vibekit/skills/tutien/references/lore-sources.md +28 -0
  73. package/.vibekit/skills/tutien/references/privacy.md +13 -1
  74. package/.vibekit/skills/tutien/references/schema.md +32 -1
  75. package/.vibekit/skills/tutien/references/story-system.md +176 -0
  76. package/.vibekit/skills/tutien/references/vi-style-guide.md +89 -0
  77. package/.vibekit/skills/tutien/references/voice-and-mode.md +23 -8
  78. package/.vibekit/skills/tutien/scripts/catalog.mjs +17 -17
  79. package/.vibekit/skills/tutien/scripts/classify.mjs +64 -37
  80. package/.vibekit/skills/tutien/scripts/command.mjs +40 -21
  81. package/.vibekit/skills/tutien/scripts/project-profile.mjs +202 -0
  82. package/.vibekit/skills/tutien/scripts/render-report.mjs +128 -72
  83. package/.vibekit/skills/tutien/scripts/response-brief.mjs +103 -0
  84. package/.vibekit/skills/tutien/scripts/run-tutien.mjs +141 -63
  85. package/.vibekit/skills/tutien/scripts/story-ledger.mjs +241 -0
  86. package/.vibekit/skills/tutien/scripts/villains.mjs +14 -14
  87. package/CHANGELOG.md +22 -1
  88. package/README.md +5 -3
  89. package/docs/README.vi.md +5 -3
  90. package/docs/README.zh-CN.md +5 -3
  91. package/package.json +2 -2
@@ -1,13 +1,14 @@
1
- // Phase 2 renderer. buildReportModel() turns analysis JSON into a
2
- // language-neutral report model (facts only). renderMarkdown() turns that
3
- // model into prose in the active language. The renderer NEVER changes a count
4
- // from the analysis it only selects wording. Villains are Phase 3 and are
5
- // intentionally not rendered here.
1
+ // Phase 2 evidence ledger. buildReportModel() turns analysis JSON into a
2
+ // language-neutral model (facts only). renderMarkdown() provides a stable
3
+ // inspection and audit view in the active language. It is deliberately not a
4
+ // user-facing response template: the agent composes that response adaptively
5
+ // from this model and the live project/story context. The ledger NEVER changes
6
+ // a count from the analysis; it only selects wording.
6
7
 
7
8
  import { scoreReport, DIMENSIONS } from './score.mjs';
8
9
  import { detectProblems } from './catalog.mjs';
9
10
  import { buildVillains } from './villains.mjs';
10
- import { classifyProject, progressionMetrics, PROGRESSION } from './classify.mjs';
11
+ import { classifyProject, localizeRationale, progressionMetrics, PROGRESSION } from './classify.mjs';
11
12
  import { resolveTone, TUTIEN_EXPERIENCE } from './command.mjs';
12
13
 
13
14
  // A context is sensitive (humor forced neutral) when unrecovered failures or
@@ -24,6 +25,13 @@ export function isSensitive(analysis, options = {}) {
24
25
  return unrecovered || (analysis.conflicts ?? []).length > 0;
25
26
  }
26
27
 
28
+ function resolveProjectHelp(projectHelp, profile) {
29
+ if (!profile) return projectHelp;
30
+ if (projectHelp === 'backbone.yml validate') return profile.recommendedValidation ?? null;
31
+ if (['sequential-thinking', 'clearthought', 'claim'].includes(projectHelp) && profile.kitInstalled !== true) return null;
32
+ return projectHelp;
33
+ }
34
+
27
35
  export function buildReportModel(analysis, options = {}) {
28
36
  const score = scoreReport(analysis);
29
37
  const rawProblems = detectProblems(analysis);
@@ -73,19 +81,27 @@ export function buildReportModel(analysis, options = {}) {
73
81
  : problems.length
74
82
  ? problems[0].meta.ifThen
75
83
  : {
76
- vi: 'Nếu bắt đầu một task mới, thì viết tiêu chí "done" trước khi sửa code.',
84
+ vi: 'Nếu bắt đầu một nhiệm vụ mới, hãy viết tiêu chí hoàn thành trước khi sửa mã nguồn.',
77
85
  en: 'If you start a new task, then write its done-criterion before editing code.'
78
86
  };
79
87
 
88
+ const topProjectHelp = problems.length ? resolveProjectHelp(problems[0].meta.projectHelp, options.profile) : null;
80
89
  const lesson = suppressed
81
90
  ? null
82
91
  : problems.length
83
- ? { skill: problems[0].meta.projectHelp, technique: problems[0].meta.counterTechnique }
84
- : { skill: 'backbone.yml validate', technique: { vi: 'Giữ vòng lặp: sửa nhỏ, chạy validation, xác nhận.', en: 'Keep the loop: small edit, run validation, confirm.' } };
92
+ ? { skill: topProjectHelp, technique: problems[0].meta.counterTechnique }
93
+ : { skill: options.profile?.recommendedValidation ?? (options.profile ? null : 'backbone.yml validate'), technique: { vi: 'Giữ vòng lặp: sửa nhỏ, chạy kiểm chứng, xác nhận.', en: 'Keep the loop: small edit, run validation, confirm.' } };
85
94
 
86
95
  return {
87
96
  schemaVersion: 1,
88
97
  experience: { ...TUTIEN_EXPERIENCE },
98
+ composition: {
99
+ finalResponse: 'agent-authored',
100
+ ledgerRole: 'evidence-scaffold-only',
101
+ adaptTo: ['current-user-request', 'project-specific-facts', 'living-chronicle', 'recent-response-rhythm'],
102
+ preserve: ['facts', 'confidence', 'policy', 'privacy', 'continuity'],
103
+ vary: ['structure', 'opening', 'scene', 'pacing', 'dialogue', 'technical-density', 'closing-image']
104
+ },
89
105
  tone,
90
106
  villainsShown: villains.length,
91
107
  villainCards: villains,
@@ -122,7 +138,7 @@ export function buildReportModel(analysis, options = {}) {
122
138
  priority: p.priority,
123
139
  evidence: p.evidence,
124
140
  counterTechnique: p.meta.counterTechnique,
125
- projectHelp: p.meta.projectHelp,
141
+ projectHelp: resolveProjectHelp(p.meta.projectHelp, options.profile),
126
142
  microQuest: p.meta.microQuest,
127
143
  victory: p.meta.victory,
128
144
  villain: v ? { name: v.archetype.name, gloss: v.archetype.gloss, role: v.role, tone: v.tone, challenge: v.challenge } : null
@@ -135,59 +151,78 @@ export function buildReportModel(analysis, options = {}) {
135
151
 
136
152
  const L = {
137
153
  vi: {
138
- title: 'Tu Tiên Tĩnh ',
139
- neutralTitle: 'Bản Ghi Soát Quy Trình',
154
+ title: 'Tu tiên tĩnh ',
155
+ neutralTitle: 'Bản ghi soát quy trình',
140
156
  opening: {
141
157
  serene: 'Mây nhẹ qua sơn môn, một trang đạo ký lặng lẽ mở ra. Đây là cuộc dạo chơi phân loại nếp cộng tác: thở chậm, nhìn rõ, rồi chọn một bước vừa sức.',
142
158
  spirited: 'Chuông ngọc khẽ ngân nơi sơn môn; đạo ký mở sang trang mới. Ta cùng nhìn dấu vết công việc bằng một chút hóm hỉnh và một bước tiến thật rõ ràng.',
143
159
  neutral: 'Bản ghi này trình bày bằng giọng bình tĩnh, trực tiếp; phần diễn xướng tu tiên tạm dừng để ưu tiên an toàn và sự rõ ràng.'
144
160
  },
145
- thienCo: 'Thiên Trong Tầm Mắt',
146
- canhGioi: 'Cảnh Giới Bên Hiên Mây',
147
- linhThach: 'Linh Thạch Đã Tiêu',
148
- tamMa: 'Tâm Ma Trên Đạo Lộ',
149
- congPhap: 'Một Thức Cho Lần Đột Phá Tới',
150
- baiHoc: 'Công Pháp Mang Theo Hôm Nay',
151
- phuongPhap: 'Đạo Điển & Nguồn Số Liệu',
152
- closingTitle: 'Khép Lại Đạo ',
161
+ thienCo: 'Thiên trong tầm mắt',
162
+ canhGioi: 'Cảnh giới bên hiên mây',
163
+ linhThach: 'Linh thạch đã tiêu',
164
+ tamMa: 'Tâm ma trên đạo lộ',
165
+ congPhap: 'Một thức cho lần đột phá tới',
166
+ baiHoc: 'Công pháp mang theo hôm nay',
167
+ phuongPhap: 'Đạo điển nguồn số liệu',
168
+ closingTitle: 'Khép lại đạo ',
153
169
  closing: {
154
- clear: 'Đạo lộ hôm nay yên. Giữ nhịp nhỏ, kiểm chứng thật, rồi để lần tiến cảnh sau tự nhiên sáng hơn.',
155
- challenge: 'Một nút thắt đã lộ hình thì đã bớt đáng ngại. Chọn đúng một nhiệm vụ nhỏ trên, hoàn thành nó, rồi thong thả đi tiếp.',
156
- paused: 'Bản ghi dừng tại đây. Hãy xử bước review hoặc ủy quyền cần thiết trước; không có cảnh giới nào quan trọng hơn một ranh giới an toàn.'
170
+ clear: 'Đạo lộ yên không có nghĩa đạo tâm được phép ngủ quên. Giữ bước sửa nhỏ chứng cứ thật; chuông kiểm chứng vẫn treo dưới mái sơn môn, chờ lần ai đó định đi tắt.',
171
+ challenge: 'Tâm ma đã để dấu chân rõ đến thế mà còn vòng qua nó thì chỉ tự dâng thêm lương khô cho địch. Hạ đúng một nút thắt bằng kiểm chứng thật; ngoài sơn môn, bóng áo của vẫn chưa tan.',
172
+ paused: 'Bản ghi dừng tại đây. Hãy hoàn tất bước soát hoặc ủy quyền cần thiết trước; không có cảnh giới nào quan trọng hơn một ranh giới an toàn.'
157
173
  },
158
- sessions: 'phiên', prompts: 'prompt', commits: 'commit',
159
- sessionOne: 'phiên', promptOne: 'prompt', commitOne: 'commit', windowOne: 'kỳ báo cáo',
174
+ sessions: 'phiên', prompts: 'lượt yêu cầu', commits: 'bản ghi Git',
175
+ sessionOne: 'phiên', promptOne: 'lượt yêu cầu', commitOne: 'bản ghi Git', windowOne: 'kỳ báo cáo',
160
176
  window: 'Khoảng thời gian', confidence: 'Độ tin cậy',
161
- reported: 'nhà cung cấp báo cáo (chính xác)', estimated: 'ước lượng cục bộ', unknown: 'lượt không rõ',
162
- dedup: 'cập nhật streaming đã gộp',
177
+ reported: 'Số liệu do nhà cung cấp báo cáo (chính xác)', estimated: 'Ước lượng cục bộ', unknown: 'Lượt chưa số token',
178
+ dedup: 'Cập nhật luồng đã gộp',
163
179
  score: 'Điểm', evidence: 'Bằng chứng', counter: 'Cách hóa giải', quest: 'Nhiệm vụ nhỏ', victory: 'Điều kiện thắng',
164
- help: 'Trợ giúp sẵn có', ifthen: 'Nếuthì', notEnough: 'Chưa đủ thiên cơ để tính cảnh giới',
180
+ help: 'Trợ giúp sẵn có', ifthen: 'Nếuthì', notEnough: 'Chưa đủ thiên cơ để tính cảnh giới',
165
181
  noProblems: 'Không phát hiện tâm ma nào vượt ngưỡng bằng chứng. Giữ vững đạo tâm.',
166
- method: 'Phân tích tất định phiên bản 1; số đếm là candidateđộ tin cậy; lore không đổi số liệu.',
167
- repeatEv: (n) => `${n} lần lặp/thử lại trong một task (candidate).`,
168
- conflictEv: (n) => `${n} cặp chỉ thị mâu thuẫn (candidate).`,
169
- unrecEv: (n) => `${n} thất bại chưa có lần pass tương ứng.`,
170
- proofEv: (n) => `${n} commit nhưng không có sự kiện kiểm chứng.`,
171
- daoSection: 'Đạo & Truyền Thừa',
182
+ method: 'Phân tích tất định phiên bản 1. Số đếm là các dấu hiệu mức tin cậy; phần diễn giải không thay đổi số liệu.',
183
+ repeatEv: (n) => `${n} lần lặp hoặc thử lại trong một nhiệm vụ (dấu hiệu).`,
184
+ conflictEv: (n) => `${n} cặp chỉ thị mâu thuẫn (dấu hiệu).`,
185
+ unrecEv: (n) => `${n} thất bại chưa có lần kiểm chứng đạt tương ứng.`,
186
+ proofEv: (n) => `${n} bản ghi Git nhưng không có sự kiện kiểm chứng.`,
187
+ daoSection: 'Đạo truyền thừa',
172
188
  faction: 'Đạo', affiliation: 'Thân phận', pathsL: 'Đạo tu',
173
- affNote: 'Thân phận là vị thế tổ chức, không phải đạo đức Tán Tu không phải mặt đối lập của Tà Tu.',
189
+ affNote: 'Thân phận là vị thế tổ chức, không phải đạo đức. Tán Tu không phải mặt đối lập của Tà Tu.',
174
190
  tienCanh: 'Tiến cảnh',
175
191
  lifetime: 'tích lũy', windowsL: 'kỳ báo cáo',
176
- nghiepWarn: 'Nghiệp lực chưa hóa giải ưu tiên xử lý các mục còn treo ở trên.',
177
- suppressedProgression: 'Chính sách đang tạm dừng: không cộng Tu Vi, Công Đức hay các chỉ số tích cực chỉ còn Nghiệp Lực.',
192
+ nghiepWarn: 'Nghiệp lực chưa hóa giải. Hãy ưu tiên xử lý các mục còn treo ở trên.',
193
+ suppressedProgression: 'Chính sách đang tạm dừng: không cộng Tu Vi, Công Đức hay các chỉ số tích cực; chỉ còn Nghiệp Lực.',
178
194
  policyNotice: {
179
- 'declared-stop': 'Tà Đạo không phải con đường tu luyện: không cảnh giới, không Tu Vi/Công Đức, không lore. Dừng lại tìm review con người / ủy quyền hợp pháp.',
180
- 'needs-review': 'Phát hiện dấu hiệu chủ đích gây hại: chưa gán đạo, tạm dừng gamification, cần review con người trước khi tiếp tục.',
181
- 'authorization-required': 'Công việc Ma Đạo cần ủy quyền: ghi lại phạm vi engagement (authorization=<slug>) trước khi mở khóa cảnh giới và tiến cảnh.'
195
+ 'declared-stop': 'Tà Đạo không phải con đường tu luyện: không cảnh giới, không Tu Vi, không Công Đức không cốt truyện. Dừng lại để con người soát hoặc xác nhận ủy quyền hợp pháp.',
196
+ 'needs-review': 'Phát hiện dấu hiệu chủ đích gây hại: chưa gán đạo, tạm dừng phần trò chơi và cần con người rà soát trước khi tiếp tục.',
197
+ 'authorization-required': 'Công việc Ma Đạo cần ủy quyền: ghi lại phạm vi công việc bằng `authorization=<slug>` trước khi mở khóa cảnh giới và tiến cảnh.'
182
198
  },
183
199
  suppressedRealmNotice: {
184
- 'declared-stop': 'Không tính cảnh giới cho Tà Đạo. Dừng lại tìm review / ủy quyền.',
185
- 'needs-review': 'Không tính cảnh giới: cần review con người cho dấu hiệu gây hại.',
200
+ 'declared-stop': 'Không tính cảnh giới cho Tà Đạo. Dừng lại để soát hoặc xác nhận ủy quyền.',
201
+ 'needs-review': 'Không tính cảnh giới: dấu hiệu gây hại cần được con người soát.',
186
202
  'authorization-required': 'Không tính cảnh giới: công việc Ma Đạo chưa có ủy quyền được ghi nhận.'
187
203
  },
188
204
  lineageIntro: 'Ba đường phân loại dưới đây chỉ nói về dự án và nếp làm việc; không phải nhãn dán cho con người.',
189
205
  progressionIntro: 'Tiến cảnh là chiếc gương nhỏ để quan sát thói quen, không phải thước đo giá trị của đạo hữu.',
190
- problemIntro: 'Mỗi tâm ma dưới đây chỉ là tên gọi vui cho một mẫu quy trình có bằng chứng nhận diện để buông nhẹ, không phải để tự trách.'
206
+ problemIntro: 'Mỗi tâm ma dưới đây chỉ là tên gọi vui cho một mẫu quy trình có bằng chứng. Nhận diện để buông nhẹ, không phải để tự trách.',
207
+ detailSeparator: ': ',
208
+ reasonSeparator: '; ',
209
+ coverageSeparator: ', ',
210
+ knowledge: {
211
+ tamPhap: 'Tâm pháp', congPhap: 'Công pháp', biThuat: 'Bí thuật', thanThong: 'Thần thông',
212
+ phapBao: 'Pháp bảo', thuatPhap: 'Thuật pháp', daoDien: 'Bí tịch và đạo điển'
213
+ },
214
+ confidenceLevels: { high: 'cao', medium: 'vừa', low: 'thấp' },
215
+ dimensionLabels: {
216
+ delivery: 'hoàn thành', validation: 'kiểm chứng', clarity: 'rõ ràng', recovery: 'khôi phục', safety: 'an toàn', automation: 'tự động hóa'
217
+ },
218
+ roleLabels: { boss: 'đại địch', minor: 'địch thủ' },
219
+ problemLabels: {
220
+ 'repeated-failure': 'Lỗi lặp lại',
221
+ 'too-many-prompts': 'Quá nhiều lượt yêu cầu',
222
+ 'conflicting-instructions': 'Chỉ thị mâu thuẫn',
223
+ 'unrecovered-failure': 'Thất bại chưa khôi phục',
224
+ 'work-without-proof': 'Công việc thiếu bằng chứng'
225
+ }
191
226
  },
192
227
  en: {
193
228
  title: 'Quiet Cultivation Chronicle',
@@ -206,8 +241,8 @@ const L = {
206
241
  phuongPhap: 'Dao Record & Evidence Sources',
207
242
  closingTitle: 'Closing the Dao Record',
208
243
  closing: {
209
- clear: 'The path is quiet today. Keep the steps small, keep the proof honest, and let the next breakthrough arrive in its own time.',
210
- challenge: 'A knot becomes lighter once it has a name. Choose one micro-quest above, complete it, then continue without haste.',
244
+ clear: 'A quiet path is no excuse for a sleeping discipline. Keep the edits small and the proof honest; the validation bell is still hanging beneath the eaves, waiting for the next attempted shortcut.',
245
+ challenge: 'The flaw has left tracks clear enough that walking around it would only feed the enemy. Break one knot with real proof; beyond the gate, its sleeve has not yet vanished into the mist.',
211
246
  paused: 'This record pauses here. Complete the required review or authorization first; no realm matters more than a sound safety boundary.'
212
247
  },
213
248
  sessions: 'sessions', prompts: 'prompts', commits: 'commits',
@@ -242,11 +277,25 @@ const L = {
242
277
  },
243
278
  lineageIntro: 'These three classifications describe the project and its working patterns; they are never labels for the person.',
244
279
  progressionIntro: 'Progression is a small mirror for observing habits, not a measure of anyone\'s worth.',
245
- problemIntro: 'Each heart demon below is a playful name for an evidence-backed workflow pattern—something to notice and release, never a reason for self-blame.'
280
+ problemIntro: 'Each heart demon below is a playful name for an evidence-backed workflow pattern—something to notice and release, never a reason for self-blame.',
281
+ detailSeparator: ' — ',
282
+ reasonSeparator: ' — ',
283
+ coverageSeparator: ' / ',
284
+ knowledge: {
285
+ tamPhap: 'Tâm Pháp', congPhap: 'Công Pháp', biThuat: 'Bí Thuật', thanThong: 'Thần Thông',
286
+ phapBao: 'Pháp Bảo', thuatPhap: 'Thuật Pháp', daoDien: 'Bí Tịch / Đạo Điển'
287
+ },
288
+ confidenceLevels: {},
289
+ dimensionLabels: {},
290
+ roleLabels: {},
291
+ problemLabels: {}
246
292
  }
247
293
  };
248
294
 
249
295
  const pick = (obj, lang) => obj[lang] ?? obj.en ?? obj.vi;
296
+ const localized = (map, value) => map?.[String(value)] ?? value;
297
+ const joinDetail = (left, right, t) => `${left}${t.detailSeparator ?? ' — '}${right}`;
298
+ const joinReason = (left, right, t) => `${left}${t.reasonSeparator ?? ' — '}${right}`;
250
299
 
251
300
  function bar(value) {
252
301
  const filled = Math.round(value * 10);
@@ -257,28 +306,35 @@ function realmName(realm, lang) {
257
306
  return lang === 'vi' ? realm.name : `${realm.name} — ${realm.gloss}`;
258
307
  }
259
308
 
309
+ function missingReason(reason, lang) {
310
+ if (lang === 'vi' && reason === 'token usage coverage below 60%; supply provider usage metadata to compute a realm') {
311
+ return 'Độ phủ số liệu token dưới 60%; cần bổ sung dữ liệu sử dụng từ nhà cung cấp để tính cảnh giới.';
312
+ }
313
+ return reason;
314
+ }
315
+
260
316
  function classificationLines(classification, lang, t) {
261
317
  const c = classification;
262
318
  const out = [`## ${t.daoSection}`, t.lineageIntro];
263
- out.push(`- ${t.faction}: **${c.faction.name}** — ${pick(c.faction.gloss, lang)} (${t.confidence} ${c.faction.confidence})`);
319
+ out.push(`- ${t.faction}: ${joinDetail(`**${c.faction.name}**`, pick(c.faction.gloss, lang), t)} (${t.confidence} ${localized(t.confidenceLevels, c.faction.confidence)})`);
264
320
  for (const line of pick(c.explanation, lang)) out.push(` - ${line}`);
265
321
  if (c.policy.state !== 'clear') out.push(` - ${t.policyNotice[c.policy.state]}`);
266
- out.push(`- ${t.affiliation}: **${c.affiliation.name}** — ${pick(c.affiliation.gloss, lang)} (${t.confidence} ${c.affiliation.confidence}; ${c.affiliation.rationale})`);
322
+ out.push(`- ${t.affiliation}: ${joinDetail(`**${c.affiliation.name}**`, pick(c.affiliation.gloss, lang), t)} (${t.confidence} ${localized(t.confidenceLevels, c.affiliation.confidence)}; ${localizeRationale(c.affiliation.rationale, lang)})`);
267
323
  out.push(` - ${t.affNote}`);
268
324
  if (c.paths.length) {
269
- out.push(`- ${t.pathsL}: ${c.paths.map((p) => `**${p.name}** (${p.confidence})`).join(', ')}`);
270
- for (const p of c.paths) out.push(` - ${p.name}: ${pick(p.gloss, lang)} — ${p.rationale}`);
325
+ out.push(`- ${t.pathsL}: ${c.paths.map((p) => `**${p.name}** (${localized(t.confidenceLevels, p.confidence)})`).join(', ')}`);
326
+ for (const p of c.paths) out.push(` - ${p.name}: ${joinReason(pick(p.gloss, lang), localizeRationale(p.rationale, lang), t)}`);
271
327
  }
272
328
  // Knowledge recommendations only under a clear policy.
273
329
  const k = c.knowledge;
274
330
  if (k) {
275
- out.push(`- Tâm Pháp: ${pick(k.tamPhap, lang)}`);
276
- out.push(`- Công Pháp: ${pick(k.congPhap, lang)}`);
277
- out.push(`- Bí Thuật: ${pick(k.biThuat, lang)}`);
278
- out.push(`- Thần Thông: ${pick(k.thanThong, lang)}`);
279
- if (k.phapBao.length) out.push(`- Pháp Bảo: ${k.phapBao.map((s) => `\`${s}\``).join(', ')}`);
280
- out.push(`- Thuật Pháp: ${pick(k.thuatPhap, lang)}`);
281
- out.push(`- Bí Tịch / Đạo Điển: ${pick(k.daoDien, lang)}`);
331
+ out.push(`- ${t.knowledge.tamPhap}: ${pick(k.tamPhap, lang)}`);
332
+ out.push(`- ${t.knowledge.congPhap}: ${pick(k.congPhap, lang)}`);
333
+ out.push(`- ${t.knowledge.biThuat}: ${pick(k.biThuat, lang)}`);
334
+ out.push(`- ${t.knowledge.thanThong}: ${pick(k.thanThong, lang)}`);
335
+ if (k.phapBao.length) out.push(`- ${t.knowledge.phapBao}: ${k.phapBao.map((s) => `\`${s}\``).join(', ')}`);
336
+ out.push(`- ${t.knowledge.thuatPhap}: ${pick(k.thuatPhap, lang)}`);
337
+ out.push(`- ${t.knowledge.daoDien}: ${pick(k.daoDien, lang)}`);
282
338
  }
283
339
  return out;
284
340
  }
@@ -289,18 +345,18 @@ function cultivationLines(cultivation, lang, t) {
289
345
  const gamified = cultivation.classification.policy.canGamify;
290
346
  out.push('', `## ${t.tienCanh}`, t.progressionIntro);
291
347
  if (gamified) {
292
- out.push(`- ${PROGRESSION.tuVi.name}: +${pr.tuVi.window} (${t.lifetime} ${pr.tuVi.lifetime}) — ${pick(PROGRESSION.tuVi.gloss, lang)}`);
348
+ out.push(`- ${PROGRESSION.tuVi.name}: ${joinDetail(`+${pr.tuVi.window} (${t.lifetime} ${pr.tuVi.lifetime})`, pick(PROGRESSION.tuVi.gloss, lang), t)}`);
293
349
  const windowUnit = pr.daoHanh.windows === 1 ? t.windowOne : t.windowsL;
294
- out.push(`- ${PROGRESSION.daoHanh.name}: ${pr.daoHanh.windows} ${windowUnit} — ${pick(PROGRESSION.daoHanh.gloss, lang)}`);
295
- out.push(`- ${PROGRESSION.ngoTinh.name} \`${bar(pr.ngoTinh)}\` ${Math.round(pr.ngoTinh * 100)}% — ${pick(PROGRESSION.ngoTinh.gloss, lang)}`);
296
- out.push(`- ${PROGRESSION.doThuanThuc.name} \`${bar(pr.doThuanThuc.overall)}\` ${Math.round(pr.doThuanThuc.overall * 100)}% — ${pick(pr.doThuanThuc.note, lang)}`);
297
- out.push(`- ${PROGRESSION.tamCanh.name} \`${bar(pr.tamCanh)}\` ${Math.round(pr.tamCanh * 100)}% — ${pick(PROGRESSION.tamCanh.gloss, lang)}`);
298
- out.push(`- ${PROGRESSION.congDuc.name}: +${pr.congDuc.window} (${t.lifetime} ${pr.congDuc.lifetime}) — ${pick(PROGRESSION.congDuc.gloss, lang)}`);
350
+ out.push(`- ${PROGRESSION.daoHanh.name}: ${joinDetail(`${pr.daoHanh.windows} ${windowUnit}`, pick(PROGRESSION.daoHanh.gloss, lang), t)}`);
351
+ out.push(`- ${joinDetail(`${PROGRESSION.ngoTinh.name} \`${bar(pr.ngoTinh)}\` ${Math.round(pr.ngoTinh * 100)}%`, pick(PROGRESSION.ngoTinh.gloss, lang), t)}`);
352
+ out.push(`- ${joinDetail(`${PROGRESSION.doThuanThuc.name} \`${bar(pr.doThuanThuc.overall)}\` ${Math.round(pr.doThuanThuc.overall * 100)}%`, pick(pr.doThuanThuc.note, lang), t)}`);
353
+ out.push(`- ${joinDetail(`${PROGRESSION.tamCanh.name} \`${bar(pr.tamCanh)}\` ${Math.round(pr.tamCanh * 100)}%`, pick(PROGRESSION.tamCanh.gloss, lang), t)}`);
354
+ out.push(`- ${PROGRESSION.congDuc.name}: ${joinDetail(`+${pr.congDuc.window} (${t.lifetime} ${pr.congDuc.lifetime})`, pick(PROGRESSION.congDuc.gloss, lang), t)}`);
299
355
  } else {
300
356
  // Suppressed policy: only Nghiệp Lực (risk) survives; no positive gains.
301
357
  out.push(`- ${t.suppressedProgression}`);
302
358
  }
303
- out.push(`- ${PROGRESSION.nghiepLuc.name}: ${pr.nghiepLuc.window} (${t.lifetime} ${pr.nghiepLuc.lifetime}) — ${pick(PROGRESSION.nghiepLuc.gloss, lang)}`);
359
+ out.push(`- ${PROGRESSION.nghiepLuc.name}: ${joinDetail(`${pr.nghiepLuc.window} (${t.lifetime} ${pr.nghiepLuc.lifetime})`, pick(PROGRESSION.nghiepLuc.gloss, lang), t)}`);
304
360
  if (pr.nghiepLuc.window > 0) out.push(` - ${t.nghiepWarn}`);
305
361
  return out;
306
362
  }
@@ -338,21 +394,21 @@ export function renderMarkdown(model, language = 'en') {
338
394
  const sessionUnit = cov.sessions === 1 ? t.sessionOne : t.sessions;
339
395
  const promptUnit = cov.userPrompts === 1 ? t.promptOne : t.prompts;
340
396
  const commitUnit = cov.commits === 1 ? t.commitOne : t.commits;
341
- out.push(`- ${cov.sessions} ${sessionUnit} / ${cov.userPrompts} ${promptUnit} / ${cov.commits} ${commitUnit}`);
397
+ out.push(`- ${cov.sessions} ${sessionUnit}${t.coverageSeparator}${cov.userPrompts} ${promptUnit}${t.coverageSeparator}${cov.commits} ${commitUnit}`);
342
398
  out.push(`- ${t.window}: ${cov.window.start ?? '—'} … ${cov.window.end ?? '—'}`);
343
399
  out.push(`- ${t.reported}: ${Math.round(cov.reportedTurnsPct * 100)}% · ${t.estimated}: ${Math.round(cov.estimatedTurnsPct * 100)}%`);
344
- out.push(`- ${t.confidence}: ${cov.confidence}`, '');
400
+ out.push(`- ${t.confidence}: ${localized(t.confidenceLevels, cov.confidence)}`, '');
345
401
 
346
402
  out.push(`## ${t.canhGioi}`);
347
403
  if (model.suppressGamification) {
348
404
  out.push(t.suppressedRealmNotice[model.policyState] ?? t.suppressedRealmNotice['declared-stop']);
349
405
  } else if (!model.enoughEvidence) {
350
406
  out.push(`**${realmName(model.realm, lang)}** — ${t.notEnough}.`);
351
- for (const m of model.missing) out.push(`- ${m}`);
407
+ for (const m of model.missing) out.push(`- ${missingReason(m, lang)}`);
352
408
  } else {
353
409
  out.push(`**${realmName(model.realm, lang)}**${model.showScore ? ` · ${t.score}: ${model.score}/100` : ''}`);
354
410
  for (const d of model.dimensions) {
355
- out.push(`- ${d.key} \`${bar(d.value)}\` ${Math.round(d.value * 100)}% (${Math.round(d.weight * 100)}%)`);
411
+ out.push(`- ${localized(t.dimensionLabels, d.key)} \`${bar(d.value)}\` ${Math.round(d.value * 100)}% (${Math.round(d.weight * 100)}%)`);
356
412
  }
357
413
  }
358
414
  out.push('');
@@ -376,15 +432,15 @@ export function renderMarkdown(model, language = 'en') {
376
432
  for (const p of model.problems) {
377
433
  if (p.villain) {
378
434
  const label = lang === 'vi' ? p.villain.name : `${p.villain.name} — ${p.villain.gloss}`;
379
- out.push(`### ${label} (${p.villain.role}, ${t.confidence} ${p.confidence})`);
435
+ out.push(`### ${label} (${localized(t.roleLabels, p.villain.role)}, ${t.confidence} ${p.confidence})`);
380
436
  out.push(`> ${pick(p.villain.challenge, lang)}`);
381
437
  } else {
382
- out.push(`### ${p.problemType} (${t.confidence} ${p.confidence})`);
438
+ out.push(`### ${localized(t.problemLabels, p.problemType)} (${t.confidence} ${p.confidence})`);
383
439
  }
384
440
  // A challenge is always immediately followed by evidence + counter + victory.
385
441
  out.push(`- ${t.evidence}: ${problemEvidenceLine(p, t)}`);
386
442
  out.push(`- ${t.counter}: ${pick(p.counterTechnique, lang)}`);
387
- out.push(`- ${t.help}: \`${p.projectHelp}\``);
443
+ if (p.projectHelp) out.push(`- ${t.help}: \`${p.projectHelp}\``);
388
444
  out.push(`- ${t.quest}: ${pick(p.microQuest, lang)}`);
389
445
  out.push(`- ${t.victory}: ${pick(p.victory, lang)}`);
390
446
  }
@@ -395,7 +451,7 @@ export function renderMarkdown(model, language = 'en') {
395
451
  out.push(`- **${t.ifthen}:** ${pick(model.ifThen, lang)}`, '');
396
452
 
397
453
  out.push(`## ${t.baiHoc}`);
398
- out.push(`- \`${model.lesson.skill}\` ${pick(model.lesson.technique, lang)}`, '');
454
+ out.push(`- ${model.lesson.skill ? joinDetail(`\`${model.lesson.skill}\``, pick(model.lesson.technique, lang), t) : pick(model.lesson.technique, lang)}`, '');
399
455
  }
400
456
 
401
457
  out.push(`## ${t.phuongPhap}`);
@@ -0,0 +1,103 @@
1
+ // Compact, aggregate-only handoff between the deterministic analyzer and the
2
+ // agent-authored response. It carries facts and constraints, never stock prose.
3
+
4
+ const localizeMissing = (reason, language) => {
5
+ if (language !== 'vi') return reason;
6
+ if (reason === 'token usage coverage below 60%; supply provider usage metadata to compute a realm') {
7
+ return 'Độ phủ số liệu token dưới 60%; cần bổ sung dữ liệu sử dụng từ nhà cung cấp để tính cảnh giới.';
8
+ }
9
+ return reason;
10
+ };
11
+
12
+ function localized(value, language) {
13
+ if (!value || typeof value !== 'object') return value ?? null;
14
+ return value[language] ?? value.en ?? value.vi ?? null;
15
+ }
16
+
17
+ function problemEvidence(problem) {
18
+ const evidence = problem.evidence ?? {};
19
+ if (problem.problemType === 'repeated-failure' || problem.problemType === 'too-many-prompts') return { repeatCount: evidence.repeatCount ?? 0 };
20
+ if (problem.problemType === 'conflicting-instructions') return { count: evidence.count ?? 0 };
21
+ if (problem.problemType === 'unrecovered-failure') return { count: evidence.count ?? 0 };
22
+ if (problem.problemType === 'work-without-proof') return { commits: evidence.commits ?? 0 };
23
+ return {};
24
+ }
25
+
26
+ export function buildResponseBrief(model, options = {}) {
27
+ const language = options.language === 'vi' ? 'vi' : 'en';
28
+ const profile = options.profile ?? {};
29
+ const classification = model.cultivation?.classification ?? null;
30
+ const progression = model.cultivation?.progression ?? null;
31
+ return {
32
+ schema: 'tutien-response-brief-v1',
33
+ semanticNamespace: model.experience.semanticNamespace,
34
+ language,
35
+ tone: model.tone,
36
+ policy: {
37
+ state: model.policyState,
38
+ suppressGamification: model.suppressGamification
39
+ },
40
+ project: {
41
+ id: profile.projectId ?? 'repo',
42
+ type: profile.projectType ?? null,
43
+ primaryLanguage: profile.primaryLanguage ?? null,
44
+ domains: profile.domains ?? [],
45
+ stack: profile.stack ?? [],
46
+ packageManager: profile.packageManager ?? null,
47
+ validationCommands: profile.validationCommands ?? [],
48
+ recommendedValidation: profile.recommendedValidation ?? null,
49
+ metadataSources: profile.metadataSources ?? [],
50
+ kitInstalled: profile.kitInstalled === true
51
+ },
52
+ evidence: {
53
+ coverage: model.coverage,
54
+ tokens: model.tokens,
55
+ realm: model.realm ? { name: model.realm.name, score: model.score } : null,
56
+ enoughEvidence: model.enoughEvidence,
57
+ missing: (model.missing ?? []).map((reason) => localizeMissing(reason, language)),
58
+ dimensions: model.dimensions
59
+ },
60
+ cultivation: classification ? {
61
+ faction: classification.faction?.id ?? null,
62
+ affiliation: classification.affiliation?.id ?? null,
63
+ paths: (classification.paths ?? []).map((item) => item.id),
64
+ progression: progression ? {
65
+ tuVi: progression.tuVi?.window ?? 0,
66
+ congDuc: progression.congDuc?.window ?? 0,
67
+ nghiepLuc: progression.nghiepLuc?.window ?? 0,
68
+ daoHanhWindows: progression.daoHanh?.windows ?? 0
69
+ } : null
70
+ } : null,
71
+ findings: (model.problems ?? []).map((problem) => ({
72
+ type: problem.problemType,
73
+ confidence: problem.confidence,
74
+ priority: problem.priority,
75
+ evidence: problemEvidence(problem),
76
+ counterTechnique: localized(problem.counterTechnique, language),
77
+ projectHelp: problem.projectHelp ?? null,
78
+ microQuest: localized(problem.microQuest, language),
79
+ victory: localized(problem.victory, language)
80
+ })),
81
+ nextPractice: model.ifThen ? localized(model.ifThen, language) : null,
82
+ story: options.storyContext ? {
83
+ evidenceKey: options.storyContext.evidenceKey,
84
+ language: options.storyContext.language,
85
+ style: options.storyContext.style,
86
+ focus: options.storyContext.focus,
87
+ canWriteChapter: options.storyContext.canWriteChapter
88
+ } : null,
89
+ artifacts: { evidenceLedger: options.ledgerPath ?? null },
90
+ composition: {
91
+ finalResponse: 'agent-authored',
92
+ evidenceLedgerIsNotFinalResponse: true,
93
+ deriveFrom: ['current-user-request', 'project-facts', 'approved-evidence', 'living-chronicle', 'recent-response-rhythm'],
94
+ preserve: model.composition.preserve,
95
+ vary: model.composition.vary,
96
+ prohibitions: ['stock-heading-sequence', 'stock-opening', 'stock-closing', 'raw-prompt-text', 'raw-event-ids'],
97
+ vietnameseEndingIntent: language === 'vi'
98
+ ? 'Kết cà khịa, luôn giáo huấn, không tâng bốc và kéo dài dư âm vai diễn.'
99
+ : null
100
+ }
101
+ };
102
+ }
103
+