minimal-vibe-coding-kit 0.4.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (207) hide show
  1. package/.agents/skills/agentshield-security-review/scripts/agentshield_repo_probe.py +28 -3
  2. package/.agents/skills/claim/SKILL.md +82 -0
  3. package/.agents/skills/prompt-sharpener/SKILL.md +54 -0
  4. package/.agents/skills/tutien/SKILL.md +74 -0
  5. package/.agents/skills/tutien/references/classification.md +64 -0
  6. package/.agents/skills/tutien/references/lore-sources.md +15 -0
  7. package/.agents/skills/tutien/references/privacy.md +33 -0
  8. package/.agents/skills/tutien/references/schema.md +75 -0
  9. package/.agents/skills/tutien/references/scoring-and-realms.md +37 -0
  10. package/.agents/skills/tutien/references/voice-and-mode.md +44 -0
  11. package/.agents/skills/tutien/scripts/adapters/generic-jsonl.mjs +82 -0
  12. package/.agents/skills/tutien/scripts/adapters/git.mjs +48 -0
  13. package/.agents/skills/tutien/scripts/adapters/plain-transcript.mjs +37 -0
  14. package/.agents/skills/tutien/scripts/analyze-history.mjs +84 -0
  15. package/.agents/skills/tutien/scripts/catalog.mjs +130 -0
  16. package/.agents/skills/tutien/scripts/classify.mjs +311 -0
  17. package/.agents/skills/tutien/scripts/command.mjs +107 -0
  18. package/.agents/skills/tutien/scripts/compare.mjs +41 -0
  19. package/.agents/skills/tutien/scripts/metrics.mjs +306 -0
  20. package/.agents/skills/tutien/scripts/normalize-events.mjs +68 -0
  21. package/.agents/skills/tutien/scripts/redact.mjs +30 -0
  22. package/.agents/skills/tutien/scripts/render-report.mjs +423 -0
  23. package/.agents/skills/tutien/scripts/run-tutien.mjs +272 -0
  24. package/.agents/skills/tutien/scripts/score.mjs +97 -0
  25. package/.agents/skills/tutien/scripts/snapshot.mjs +76 -0
  26. package/.agents/skills/tutien/scripts/villains.mjs +196 -0
  27. package/.agents/skills/vibekit-init/SKILL.md +2 -2
  28. package/.claude/settings.json +10 -3
  29. package/.claude/skills/agentshield-security-review/scripts/agentshield_repo_probe.py +28 -3
  30. package/.claude/skills/claim/SKILL.md +82 -0
  31. package/.claude/skills/prompt-sharpener/SKILL.md +54 -0
  32. package/.claude/skills/tutien/SKILL.md +74 -0
  33. package/.claude/skills/tutien/references/classification.md +64 -0
  34. package/.claude/skills/tutien/references/lore-sources.md +15 -0
  35. package/.claude/skills/tutien/references/privacy.md +33 -0
  36. package/.claude/skills/tutien/references/schema.md +75 -0
  37. package/.claude/skills/tutien/references/scoring-and-realms.md +37 -0
  38. package/.claude/skills/tutien/references/voice-and-mode.md +44 -0
  39. package/.claude/skills/tutien/scripts/adapters/generic-jsonl.mjs +82 -0
  40. package/.claude/skills/tutien/scripts/adapters/git.mjs +48 -0
  41. package/.claude/skills/tutien/scripts/adapters/plain-transcript.mjs +37 -0
  42. package/.claude/skills/tutien/scripts/analyze-history.mjs +84 -0
  43. package/.claude/skills/tutien/scripts/catalog.mjs +130 -0
  44. package/.claude/skills/tutien/scripts/classify.mjs +311 -0
  45. package/.claude/skills/tutien/scripts/command.mjs +107 -0
  46. package/.claude/skills/tutien/scripts/compare.mjs +41 -0
  47. package/.claude/skills/tutien/scripts/metrics.mjs +306 -0
  48. package/.claude/skills/tutien/scripts/normalize-events.mjs +68 -0
  49. package/.claude/skills/tutien/scripts/redact.mjs +30 -0
  50. package/.claude/skills/tutien/scripts/render-report.mjs +423 -0
  51. package/.claude/skills/tutien/scripts/run-tutien.mjs +272 -0
  52. package/.claude/skills/tutien/scripts/score.mjs +97 -0
  53. package/.claude/skills/tutien/scripts/snapshot.mjs +76 -0
  54. package/.claude/skills/tutien/scripts/villains.mjs +196 -0
  55. package/.claude/skills/vibekit-init/SKILL.md +2 -2
  56. package/.codex/rules/vibekit.rules +77 -0
  57. package/.codex-plugin/plugin.json +1 -1
  58. package/.cursor/cli.json +18 -0
  59. package/.cursor/settings.json +15 -8
  60. package/.cursor/skills/claim/SKILL.md +82 -0
  61. package/.cursor/skills/prompt-sharpener/SKILL.md +54 -0
  62. package/.cursor/skills/tutien/SKILL.md +74 -0
  63. package/.cursor/skills/tutien/references/classification.md +64 -0
  64. package/.cursor/skills/tutien/references/lore-sources.md +15 -0
  65. package/.cursor/skills/tutien/references/privacy.md +33 -0
  66. package/.cursor/skills/tutien/references/schema.md +75 -0
  67. package/.cursor/skills/tutien/references/scoring-and-realms.md +37 -0
  68. package/.cursor/skills/tutien/references/voice-and-mode.md +44 -0
  69. package/.cursor/skills/tutien/scripts/adapters/generic-jsonl.mjs +82 -0
  70. package/.cursor/skills/tutien/scripts/adapters/git.mjs +48 -0
  71. package/.cursor/skills/tutien/scripts/adapters/plain-transcript.mjs +37 -0
  72. package/.cursor/skills/tutien/scripts/analyze-history.mjs +84 -0
  73. package/.cursor/skills/tutien/scripts/catalog.mjs +130 -0
  74. package/.cursor/skills/tutien/scripts/classify.mjs +311 -0
  75. package/.cursor/skills/tutien/scripts/command.mjs +107 -0
  76. package/.cursor/skills/tutien/scripts/compare.mjs +41 -0
  77. package/.cursor/skills/tutien/scripts/metrics.mjs +306 -0
  78. package/.cursor/skills/tutien/scripts/normalize-events.mjs +68 -0
  79. package/.cursor/skills/tutien/scripts/redact.mjs +30 -0
  80. package/.cursor/skills/tutien/scripts/render-report.mjs +423 -0
  81. package/.cursor/skills/tutien/scripts/run-tutien.mjs +272 -0
  82. package/.cursor/skills/tutien/scripts/score.mjs +97 -0
  83. package/.cursor/skills/tutien/scripts/snapshot.mjs +76 -0
  84. package/.cursor/skills/tutien/scripts/villains.mjs +196 -0
  85. package/.grok/README.md +13 -0
  86. package/.grok/config.example.toml +13 -0
  87. package/.grok/config.toml +29 -0
  88. package/.grok/rules/safe-delete.md +11 -0
  89. package/.grok/rules/security.md +5 -0
  90. package/.grok/rules/vibe-core.md +8 -0
  91. package/.grok/skills/agentshield-security-review/SKILL.md +75 -0
  92. package/.grok/skills/agentshield-security-review/agents/openai.yaml +5 -0
  93. package/.grok/skills/agentshield-security-review/references/native-install.md +97 -0
  94. package/.grok/skills/agentshield-security-review/references/report-template.md +54 -0
  95. package/.grok/skills/agentshield-security-review/references/review-checklist.md +45 -0
  96. package/.grok/skills/agentshield-security-review/scripts/agentshield_repo_probe.py +135 -0
  97. package/.grok/skills/autoresearch-coding/README.md +15 -0
  98. package/.grok/skills/autoresearch-coding/SKILL.md +101 -0
  99. package/.grok/skills/autoresearch-coding/references/experiment-contract.md +13 -0
  100. package/.grok/skills/autoresearch-coding/references/metric-extraction.md +8 -0
  101. package/.grok/skills/autoresearch-coding/references/result-ledger.md +15 -0
  102. package/.grok/skills/autoresearch-coding/scripts/log_result.py +24 -0
  103. package/.grok/skills/autoresearch-coding/scripts/run_logged.py +30 -0
  104. package/.grok/skills/claim/SKILL.md +82 -0
  105. package/.grok/skills/clearthought/SKILL.md +100 -0
  106. package/.grok/skills/clearthought/examples/decision-framework.md +441 -0
  107. package/.grok/skills/clearthought/examples/metagame-examples.md +536 -0
  108. package/.grok/skills/clearthought/examples/sequential-thinking.md +380 -0
  109. package/.grok/skills/clearthought/references/output-schemas.md +494 -0
  110. package/.grok/skills/clearthought/references/parameter-reference.md +482 -0
  111. package/.grok/skills/coding-level/SKILL.md +34 -0
  112. package/.grok/skills/coding-level/references/level-0.md +131 -0
  113. package/.grok/skills/coding-level/references/level-1.md +118 -0
  114. package/.grok/skills/coding-level/references/level-2.md +140 -0
  115. package/.grok/skills/coding-level/references/level-3.md +142 -0
  116. package/.grok/skills/coding-level/references/level-4.md +152 -0
  117. package/.grok/skills/coding-level/references/level-5.md +84 -0
  118. package/.grok/skills/daily-workflow-curator/SKILL.md +52 -0
  119. package/.grok/skills/memento/SKILL.md +36 -0
  120. package/.grok/skills/parallel-analysis/SKILL.md +160 -0
  121. package/.grok/skills/path-sensitive-shell-safety/SKILL.md +69 -0
  122. package/.grok/skills/path-sensitive-shell-safety/references/workflow.md +127 -0
  123. package/.grok/skills/prompt-sharpener/SKILL.md +54 -0
  124. package/.grok/skills/reviewing-4p-priorities/SKILL.md +72 -0
  125. package/.grok/skills/reviewing-4p-priorities/examples.md +298 -0
  126. package/.grok/skills/sequential-thinking/SKILL.md +106 -0
  127. package/.grok/skills/sequential-thinking/examples/adaptive-depth.md +542 -0
  128. package/.grok/skills/sequential-thinking/examples/branching-exploration.md +474 -0
  129. package/.grok/skills/sequential-thinking/examples/linear-reasoning.md +210 -0
  130. package/.grok/skills/sequential-thinking/examples/revision-pattern.md +304 -0
  131. package/.grok/skills/sequential-thinking/references/output-schema.md +483 -0
  132. package/.grok/skills/sequential-thinking/references/parameters.md +463 -0
  133. package/.grok/skills/sequential-thinking/references/patterns.md +666 -0
  134. package/.grok/skills/tutien/SKILL.md +74 -0
  135. package/.grok/skills/tutien/references/classification.md +64 -0
  136. package/.grok/skills/tutien/references/lore-sources.md +15 -0
  137. package/.grok/skills/tutien/references/privacy.md +33 -0
  138. package/.grok/skills/tutien/references/schema.md +75 -0
  139. package/.grok/skills/tutien/references/scoring-and-realms.md +37 -0
  140. package/.grok/skills/tutien/references/voice-and-mode.md +44 -0
  141. package/.grok/skills/tutien/scripts/adapters/generic-jsonl.mjs +82 -0
  142. package/.grok/skills/tutien/scripts/adapters/git.mjs +48 -0
  143. package/.grok/skills/tutien/scripts/adapters/plain-transcript.mjs +37 -0
  144. package/.grok/skills/tutien/scripts/analyze-history.mjs +84 -0
  145. package/.grok/skills/tutien/scripts/catalog.mjs +130 -0
  146. package/.grok/skills/tutien/scripts/classify.mjs +311 -0
  147. package/.grok/skills/tutien/scripts/command.mjs +107 -0
  148. package/.grok/skills/tutien/scripts/compare.mjs +41 -0
  149. package/.grok/skills/tutien/scripts/metrics.mjs +306 -0
  150. package/.grok/skills/tutien/scripts/normalize-events.mjs +68 -0
  151. package/.grok/skills/tutien/scripts/redact.mjs +30 -0
  152. package/.grok/skills/tutien/scripts/render-report.mjs +423 -0
  153. package/.grok/skills/tutien/scripts/run-tutien.mjs +272 -0
  154. package/.grok/skills/tutien/scripts/score.mjs +97 -0
  155. package/.grok/skills/tutien/scripts/snapshot.mjs +76 -0
  156. package/.grok/skills/tutien/scripts/villains.mjs +196 -0
  157. package/.grok/skills/vibekit-init/SKILL.md +52 -0
  158. package/.grok/skills/visual-design-loop/SKILL.md +103 -0
  159. package/.grok/skills/visual-design-loop/agents/openai.yaml +4 -0
  160. package/.vibekit/docs/BACKBONE_REFERENCE.md +3 -1
  161. package/.vibekit/docs/INSTALL.md +11 -7
  162. package/.vibekit/docs/SECURITY_MODEL.md +1 -1
  163. package/.vibekit/docs/TOOLING_GUIDE.md +5 -0
  164. package/.vibekit/docs/templates/CONTEXT_TEMPLATE.md +1 -1
  165. package/.vibekit/init/CLAUDE-template.md +3 -0
  166. package/.vibekit/init/FIRST_PROMPT.md +1 -1
  167. package/.vibekit/init/FIRST_TIME_INIT.md +2 -2
  168. package/.vibekit/scripts/doctor.mjs +11 -4
  169. package/.vibekit/scripts/mvck.mjs +27 -12
  170. package/.vibekit/scripts/pack-dry-run.mjs +38 -3
  171. package/.vibekit/scripts/test-install.mjs +8 -0
  172. package/.vibekit/scripts/validate-kit.mjs +116 -41
  173. package/.vibekit/skills/agentshield-security-review/scripts/agentshield_repo_probe.py +28 -3
  174. package/.vibekit/skills/claim/SKILL.md +82 -0
  175. package/.vibekit/skills/prompt-sharpener/SKILL.md +54 -0
  176. package/.vibekit/skills/skills-manifest.json +27 -0
  177. package/.vibekit/skills/tutien/SKILL.md +74 -0
  178. package/.vibekit/skills/tutien/references/classification.md +64 -0
  179. package/.vibekit/skills/tutien/references/lore-sources.md +15 -0
  180. package/.vibekit/skills/tutien/references/privacy.md +33 -0
  181. package/.vibekit/skills/tutien/references/schema.md +75 -0
  182. package/.vibekit/skills/tutien/references/scoring-and-realms.md +37 -0
  183. package/.vibekit/skills/tutien/references/voice-and-mode.md +44 -0
  184. package/.vibekit/skills/tutien/scripts/adapters/generic-jsonl.mjs +82 -0
  185. package/.vibekit/skills/tutien/scripts/adapters/git.mjs +48 -0
  186. package/.vibekit/skills/tutien/scripts/adapters/plain-transcript.mjs +37 -0
  187. package/.vibekit/skills/tutien/scripts/analyze-history.mjs +84 -0
  188. package/.vibekit/skills/tutien/scripts/catalog.mjs +130 -0
  189. package/.vibekit/skills/tutien/scripts/classify.mjs +311 -0
  190. package/.vibekit/skills/tutien/scripts/command.mjs +107 -0
  191. package/.vibekit/skills/tutien/scripts/compare.mjs +41 -0
  192. package/.vibekit/skills/tutien/scripts/metrics.mjs +306 -0
  193. package/.vibekit/skills/tutien/scripts/normalize-events.mjs +68 -0
  194. package/.vibekit/skills/tutien/scripts/redact.mjs +30 -0
  195. package/.vibekit/skills/tutien/scripts/render-report.mjs +423 -0
  196. package/.vibekit/skills/tutien/scripts/run-tutien.mjs +272 -0
  197. package/.vibekit/skills/tutien/scripts/score.mjs +97 -0
  198. package/.vibekit/skills/tutien/scripts/snapshot.mjs +76 -0
  199. package/.vibekit/skills/tutien/scripts/villains.mjs +196 -0
  200. package/.vibekit/skills/vibekit-init/SKILL.md +2 -2
  201. package/AGENTS.md +2 -2
  202. package/CHANGELOG.md +40 -0
  203. package/README.md +60 -51
  204. package/backbone.yml +2 -1
  205. package/docs/README.vi.md +61 -52
  206. package/docs/README.zh-CN.md +277 -0
  207. package/package.json +15 -4
@@ -0,0 +1,423 @@
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.
6
+
7
+ import { scoreReport, DIMENSIONS } from './score.mjs';
8
+ import { detectProblems } from './catalog.mjs';
9
+ import { buildVillains } from './villains.mjs';
10
+ import { classifyProject, progressionMetrics, PROGRESSION } from './classify.mjs';
11
+ import { resolveTone, TUTIEN_EXPERIENCE } from './command.mjs';
12
+
13
+ // A context is sensitive (humor forced neutral) when unrecovered failures or
14
+ // conflicts are present, or when the caller passes an emergency signal
15
+ // (secret exposure, security finding, outage, data loss, user distress).
16
+ export function isSensitive(analysis, options = {}) {
17
+ if (options.emergency) return true;
18
+ const signals = options.signals ?? {};
19
+ if (signals.secretExposure || signals.securityHighFinding || signals.outage || signals.dataLoss || signals.userDistress) return true;
20
+ const issues = analysis.issues ?? {};
21
+ const failures = issues.failures ?? [];
22
+ const recoveries = issues.recoveries ?? [];
23
+ const unrecovered = failures.some((f) => !recoveries.some((r) => r.issueEventId === f.eventId));
24
+ return unrecovered || (analysis.conflicts ?? []).length > 0;
25
+ }
26
+
27
+ export function buildReportModel(analysis, options = {}) {
28
+ const score = scoreReport(analysis);
29
+ const rawProblems = detectProblems(analysis);
30
+ const tokens = analysis.tokens ?? {};
31
+ const coverage = analysis.coverage ?? {};
32
+
33
+ // Optional cultivation classification from a project profile. One
34
+ // fail-closed policy state governs the whole report: only `clear` enables
35
+ // realm/score/villains/recommendations/positive progression. Everything
36
+ // else (declared-stop, needs-review, authorization-required) suppresses
37
+ // them at the DATA level — score/realm/dimensions become null, not hidden.
38
+ let cultivation = null;
39
+ let canGamify = true;
40
+ if (options.profile) {
41
+ const classification = classifyProject(options.profile);
42
+ canGamify = classification.policy.canGamify;
43
+ const progression = progressionMetrics(analysis, {
44
+ prior: options.priorProgression ?? null,
45
+ canGamify,
46
+ salt: options.progressionSalt ?? ''
47
+ });
48
+ cultivation = { classification, progression };
49
+ }
50
+ const suppressed = !canGamify;
51
+
52
+ const sensitive = isSensitive(analysis, options) || suppressed;
53
+ const tone = resolveTone(options.tone, { sensitive });
54
+ const { villains } = buildVillains(rawProblems, {
55
+ tone,
56
+ villains: options.villains ?? 'on',
57
+ threshold: options.villainThreshold,
58
+ emergency: sensitive,
59
+ seedInputs: options.seedInputs,
60
+ context: options.context,
61
+ priorVillains: options.priorVillains
62
+ });
63
+ const villainByProblem = new Map(villains.map((v) => [v.problemId, v]));
64
+
65
+ // Recommendations and antagonist framing are part of the game layer. A
66
+ // non-clear policy keeps only neutral evidence/risks, so these are absent at
67
+ // the data level rather than merely hidden by the renderer.
68
+ const problems = suppressed ? [] : rawProblems;
69
+ // One implementation-intention practice, specific to the top problem's
70
+ // catalog entry — a conflict gets a conflict rule, not a retry rule.
71
+ const ifThen = suppressed
72
+ ? null
73
+ : problems.length
74
+ ? problems[0].meta.ifThen
75
+ : {
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.',
77
+ en: 'If you start a new task, then write its done-criterion before editing code.'
78
+ };
79
+
80
+ const lesson = suppressed
81
+ ? null
82
+ : 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.' } };
85
+
86
+ return {
87
+ schemaVersion: 1,
88
+ experience: { ...TUTIEN_EXPERIENCE },
89
+ tone,
90
+ villainsShown: villains.length,
91
+ villainCards: villains,
92
+ cultivation,
93
+ policyState: cultivation?.classification.policy.state ?? 'clear',
94
+ suppressGamification: suppressed,
95
+ showScore: options.score === 'show' && !suppressed,
96
+ coverage: {
97
+ sessions: coverage.sessions ?? 0,
98
+ userPrompts: coverage.userPrompts ?? 0,
99
+ commits: coverage.commits ?? 0,
100
+ window: coverage.window ?? { start: null, end: null },
101
+ reportedTurnsPct: coverage.tokenCoverage?.reportedTurnsPct ?? 0,
102
+ estimatedTurnsPct: coverage.tokenCoverage?.estimatedTurnsPct ?? 0,
103
+ confidence: coverage.confidence ?? 'low'
104
+ },
105
+ tokens: {
106
+ reportedTotal: tokens.reportedTotal ?? 0,
107
+ estimatedTotal: tokens.estimatedTotal ?? 0,
108
+ unknownTurns: tokens.unknownTurns ?? 0,
109
+ dedupedStreamUpdates: tokens.dedupedStreamUpdates ?? 0
110
+ },
111
+ // Under a suppressed policy these are null (absent), not merely hidden.
112
+ realm: suppressed ? null : score.realm,
113
+ enoughEvidence: suppressed ? false : score.enoughEvidence,
114
+ score: suppressed ? null : score.score,
115
+ dimensions: suppressed ? [] : DIMENSIONS.map((d) => ({ key: d.key, weight: d.weight, value: score.dimensions[d.key] })),
116
+ missing: suppressed ? [] : score.missing,
117
+ problems: problems.map((p) => {
118
+ const v = villainByProblem.get(p.problemId);
119
+ return {
120
+ problemType: p.meta.problemType,
121
+ confidence: p.confidence,
122
+ priority: p.priority,
123
+ evidence: p.evidence,
124
+ counterTechnique: p.meta.counterTechnique,
125
+ projectHelp: p.meta.projectHelp,
126
+ microQuest: p.meta.microQuest,
127
+ victory: p.meta.victory,
128
+ villain: v ? { name: v.archetype.name, gloss: v.archetype.gloss, role: v.role, tone: v.tone, challenge: v.challenge } : null
129
+ };
130
+ }),
131
+ ifThen,
132
+ lesson
133
+ };
134
+ }
135
+
136
+ const L = {
137
+ vi: {
138
+ title: 'Tu Tiên Tĩnh Ký',
139
+ neutralTitle: 'Bản Ghi Rà Soát Quy Trình',
140
+ opening: {
141
+ 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
+ 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
+ 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
+ },
145
+ thienCo: 'Thiên Cơ 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 Ký',
153
+ 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ử lý 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.'
157
+ },
158
+ sessions: 'phiên', prompts: 'prompt', commits: 'commit',
159
+ sessionOne: 'phiên', promptOne: 'prompt', commitOne: 'commit', windowOne: 'kỳ báo cáo',
160
+ 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',
163
+ 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ếu—thì', notEnough: 'Chưa đủ thiên cơ để tính cảnh giới',
165
+ 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 có độ 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',
172
+ 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.',
174
+ tienCanh: 'Tiến cảnh',
175
+ 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.',
178
+ 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 và 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.'
182
+ },
183
+ suppressedRealmNotice: {
184
+ 'declared-stop': 'Không tính cảnh giới cho Tà Đạo. Dừng lại và 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.',
186
+ '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
+ },
188
+ 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
+ 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.'
191
+ },
192
+ en: {
193
+ title: 'Quiet Cultivation Chronicle',
194
+ neutralTitle: 'Coding Workflow Review',
195
+ opening: {
196
+ serene: 'Mist drifts past the mountain gate, and a quiet page of the Dao record opens. This is a mindful game of sorting collaboration patterns: breathe, observe, then choose one measured step.',
197
+ spirited: 'A jade bell sounds softly at the mountain gate; the Dao record turns a fresh page. We will meet the evidence with light wit and leave with one clear step forward.',
198
+ neutral: 'This record uses calm, direct language; cultivation theatrics are paused so safety and clarity come first.'
199
+ },
200
+ thienCo: 'Signs Beneath the Quiet Sky',
201
+ canhGioi: 'Realm at the Mountain Gate',
202
+ linhThach: 'Spirit Stones Spent (Tokens)',
203
+ tamMa: 'Heart Demons Along the Dao Path',
204
+ congPhap: 'One Technique for the Next Breakthrough',
205
+ baiHoc: "Today's Practice to Carry Forward",
206
+ phuongPhap: 'Dao Record & Evidence Sources',
207
+ closingTitle: 'Closing the Dao Record',
208
+ 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.',
211
+ paused: 'This record pauses here. Complete the required review or authorization first; no realm matters more than a sound safety boundary.'
212
+ },
213
+ sessions: 'sessions', prompts: 'prompts', commits: 'commits',
214
+ sessionOne: 'session', promptOne: 'prompt', commitOne: 'commit', windowOne: 'reporting window',
215
+ window: 'Window', confidence: 'Confidence',
216
+ reported: 'provider-reported (exact)', estimated: 'locally estimated', unknown: 'unknown turns',
217
+ dedup: 'streaming updates deduplicated',
218
+ score: 'Score', evidence: 'Evidence', counter: 'Counter-technique', quest: 'Micro-quest', victory: 'Victory',
219
+ help: 'Project help', ifthen: 'If—then', notEnough: 'Not enough evidence to compute a realm yet',
220
+ noProblems: 'No heart demon crossed the evidence threshold. Hold your Dao heart.',
221
+ method: 'Deterministic analysis v1; counts are confidence-scored candidates; lore never changes the numbers.',
222
+ repeatEv: (n) => `${n} repeats/retries within one task (candidate).`,
223
+ conflictEv: (n) => `${n} contradictory instruction pairs (candidate).`,
224
+ unrecEv: (n) => `${n} failures without a matching later pass.`,
225
+ proofEv: (n) => `${n} commits but no validation event.`,
226
+ daoSection: 'Dao & Lineage',
227
+ faction: 'Faction', affiliation: 'Affiliation', pathsL: 'Cultivation paths',
228
+ affNote: 'Affiliation is organizational status, not ethics — Tán Tu is not the opposite of Tà Tu.',
229
+ tienCanh: 'Progression',
230
+ lifetime: 'lifetime', windowsL: 'reporting windows',
231
+ nghiepWarn: 'Unresolved Nghiệp Lực — clear the open items above first.',
232
+ suppressedProgression: 'Policy is withholding gains: no Tu Vi, Công Đức, or positive metrics — only Nghiệp Lực (risk) remains.',
233
+ policyNotice: {
234
+ 'declared-stop': 'Tà Đạo is not a cultivation path: no realm, no Tu Vi/Công Đức, no lore. Stop and seek human review / lawful authorization.',
235
+ 'needs-review': 'Intent-to-harm signals detected: faction undetermined, gamification withheld, human review required before proceeding.',
236
+ 'authorization-required': 'Ma Đạo work requires authorization: record the engagement scope (authorization=<slug>) before any realm or progression is unlocked.'
237
+ },
238
+ suppressedRealmNotice: {
239
+ 'declared-stop': 'No realm is computed for Tà Đạo. Stop and seek review / authorization.',
240
+ 'needs-review': 'No realm computed: harmful-intent signals require human review.',
241
+ 'authorization-required': 'No realm computed: Ma Đạo work has no recorded authorization.'
242
+ },
243
+ lineageIntro: 'These three classifications describe the project and its working patterns; they are never labels for the person.',
244
+ 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.'
246
+ }
247
+ };
248
+
249
+ const pick = (obj, lang) => obj[lang] ?? obj.en ?? obj.vi;
250
+
251
+ function bar(value) {
252
+ const filled = Math.round(value * 10);
253
+ return '█'.repeat(filled) + '░'.repeat(10 - filled);
254
+ }
255
+
256
+ function realmName(realm, lang) {
257
+ return lang === 'vi' ? realm.name : `${realm.name} — ${realm.gloss}`;
258
+ }
259
+
260
+ function classificationLines(classification, lang, t) {
261
+ const c = classification;
262
+ const out = [`## ${t.daoSection}`, t.lineageIntro];
263
+ out.push(`- ${t.faction}: **${c.faction.name}** — ${pick(c.faction.gloss, lang)} (${t.confidence} ${c.faction.confidence})`);
264
+ for (const line of pick(c.explanation, lang)) out.push(` - ${line}`);
265
+ 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})`);
267
+ out.push(` - ${t.affNote}`);
268
+ 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}`);
271
+ }
272
+ // Knowledge recommendations only under a clear policy.
273
+ const k = c.knowledge;
274
+ 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)}`);
282
+ }
283
+ return out;
284
+ }
285
+
286
+ function cultivationLines(cultivation, lang, t) {
287
+ const out = classificationLines(cultivation.classification, lang, t);
288
+ const pr = cultivation.progression;
289
+ const gamified = cultivation.classification.policy.canGamify;
290
+ out.push('', `## ${t.tienCanh}`, t.progressionIntro);
291
+ if (gamified) {
292
+ out.push(`- ${PROGRESSION.tuVi.name}: +${pr.tuVi.window} (${t.lifetime} ${pr.tuVi.lifetime}) — ${pick(PROGRESSION.tuVi.gloss, lang)}`);
293
+ 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)}`);
299
+ } else {
300
+ // Suppressed policy: only Nghiệp Lực (risk) survives; no positive gains.
301
+ out.push(`- ${t.suppressedProgression}`);
302
+ }
303
+ out.push(`- ${PROGRESSION.nghiepLuc.name}: ${pr.nghiepLuc.window} (${t.lifetime} ${pr.nghiepLuc.lifetime}) — ${pick(PROGRESSION.nghiepLuc.gloss, lang)}`);
304
+ if (pr.nghiepLuc.window > 0) out.push(` - ${t.nghiepWarn}`);
305
+ return out;
306
+ }
307
+
308
+ // Standalone classification markdown for the `classify` runner action (no
309
+ // history analysis involved — profile only).
310
+ export function renderClassificationMarkdown(classification, language = 'en') {
311
+ const lang = L[language] ? language : 'en';
312
+ return classificationLines(classification, lang, L[lang]).join('\n');
313
+ }
314
+
315
+ function problemEvidenceLine(p, t) {
316
+ const e = p.evidence;
317
+ switch (p.problemType) {
318
+ case 'repeated-failure':
319
+ case 'too-many-prompts': return t.repeatEv(e.repeatCount);
320
+ case 'conflicting-instructions': return t.conflictEv(e.count);
321
+ case 'unrecovered-failure': return t.unrecEv(e.count);
322
+ case 'work-without-proof': return t.proofEv(e.commits);
323
+ default: return '';
324
+ }
325
+ }
326
+
327
+ export function renderMarkdown(model, language = 'en') {
328
+ const lang = L[language] ? language : 'en';
329
+ const t = L[lang];
330
+ const cov = model.coverage;
331
+ const tok = model.tokens;
332
+ const out = [];
333
+
334
+ const title = model.suppressGamification ? t.neutralTitle : t.title;
335
+ out.push(`# ${title}`, '', `> ${t.opening[model.tone] ?? t.opening.serene}`, '');
336
+
337
+ out.push(`## ${t.thienCo}`);
338
+ const sessionUnit = cov.sessions === 1 ? t.sessionOne : t.sessions;
339
+ const promptUnit = cov.userPrompts === 1 ? t.promptOne : t.prompts;
340
+ const commitUnit = cov.commits === 1 ? t.commitOne : t.commits;
341
+ out.push(`- ${cov.sessions} ${sessionUnit} / ${cov.userPrompts} ${promptUnit} / ${cov.commits} ${commitUnit}`);
342
+ out.push(`- ${t.window}: ${cov.window.start ?? '—'} … ${cov.window.end ?? '—'}`);
343
+ out.push(`- ${t.reported}: ${Math.round(cov.reportedTurnsPct * 100)}% · ${t.estimated}: ${Math.round(cov.estimatedTurnsPct * 100)}%`);
344
+ out.push(`- ${t.confidence}: ${cov.confidence}`, '');
345
+
346
+ out.push(`## ${t.canhGioi}`);
347
+ if (model.suppressGamification) {
348
+ out.push(t.suppressedRealmNotice[model.policyState] ?? t.suppressedRealmNotice['declared-stop']);
349
+ } else if (!model.enoughEvidence) {
350
+ out.push(`**${realmName(model.realm, lang)}** — ${t.notEnough}.`);
351
+ for (const m of model.missing) out.push(`- ${m}`);
352
+ } else {
353
+ out.push(`**${realmName(model.realm, lang)}**${model.showScore ? ` · ${t.score}: ${model.score}/100` : ''}`);
354
+ for (const d of model.dimensions) {
355
+ out.push(`- ${d.key} \`${bar(d.value)}\` ${Math.round(d.value * 100)}% (${Math.round(d.weight * 100)}%)`);
356
+ }
357
+ }
358
+ out.push('');
359
+
360
+ if (model.cultivation) {
361
+ out.push(...cultivationLines(model.cultivation, lang, t), '');
362
+ }
363
+
364
+ out.push(`## ${t.linhThach}`);
365
+ out.push(`- ${t.reported}: ${tok.reportedTotal}`);
366
+ out.push(`- ${t.estimated}: ${tok.estimatedTotal}`);
367
+ out.push(`- ${t.unknown}: ${tok.unknownTurns}`);
368
+ if (tok.dedupedStreamUpdates > 0) out.push(`- ${t.dedup}: ${tok.dedupedStreamUpdates}`);
369
+ out.push('');
370
+
371
+ if (!model.suppressGamification) {
372
+ out.push(`## ${t.tamMa}`, t.problemIntro);
373
+ if (model.problems.length === 0) {
374
+ out.push(t.noProblems);
375
+ } else {
376
+ for (const p of model.problems) {
377
+ if (p.villain) {
378
+ const label = lang === 'vi' ? p.villain.name : `${p.villain.name} — ${p.villain.gloss}`;
379
+ out.push(`### ${label} (${p.villain.role}, ${t.confidence} ${p.confidence})`);
380
+ out.push(`> ${pick(p.villain.challenge, lang)}`);
381
+ } else {
382
+ out.push(`### ${p.problemType} (${t.confidence} ${p.confidence})`);
383
+ }
384
+ // A challenge is always immediately followed by evidence + counter + victory.
385
+ out.push(`- ${t.evidence}: ${problemEvidenceLine(p, t)}`);
386
+ out.push(`- ${t.counter}: ${pick(p.counterTechnique, lang)}`);
387
+ out.push(`- ${t.help}: \`${p.projectHelp}\``);
388
+ out.push(`- ${t.quest}: ${pick(p.microQuest, lang)}`);
389
+ out.push(`- ${t.victory}: ${pick(p.victory, lang)}`);
390
+ }
391
+ }
392
+ out.push('');
393
+
394
+ out.push(`## ${t.congPhap}`);
395
+ out.push(`- **${t.ifthen}:** ${pick(model.ifThen, lang)}`, '');
396
+
397
+ out.push(`## ${t.baiHoc}`);
398
+ out.push(`- \`${model.lesson.skill}\` — ${pick(model.lesson.technique, lang)}`, '');
399
+ }
400
+
401
+ out.push(`## ${t.phuongPhap}`);
402
+ out.push(`- ${t.method}`, '');
403
+
404
+ out.push(`## ${t.closingTitle}`);
405
+ const closingKey = model.suppressGamification ? 'paused' : (model.problems.length ? 'challenge' : 'clear');
406
+ out.push(t.closing[closingKey]);
407
+
408
+ return out.join('\n');
409
+ }
410
+
411
+ export function renderReport(analysis, options = {}) {
412
+ const model = buildReportModel(analysis, options);
413
+ const language = resolveLanguage(options.language, options);
414
+ return { model, language, markdown: renderMarkdown(model, language) };
415
+ }
416
+
417
+ // language=auto -> detect from the invocation text; explicit code overrides;
418
+ // fallback to conversation language then English. V1 templates: vi, en.
419
+ export function resolveLanguage(requested, { invocationText = '', conversationLanguage = 'en' } = {}) {
420
+ if (requested && requested !== 'auto') return L[requested] ? requested : 'en';
421
+ if (/[À-ỹ]/.test(invocationText) || /\b(hãy|giúp|của|và|không)\b/i.test(invocationText)) return 'vi';
422
+ return L[conversationLanguage] ? conversationLanguage : 'en';
423
+ }