minimal-vibe-coding-kit 0.4.2 → 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.
- package/.agents/skills/claim/SKILL.md +82 -0
- package/.agents/skills/tutien/SKILL.md +97 -0
- package/.agents/skills/tutien/references/adaptive-response.md +68 -0
- package/.agents/skills/tutien/references/classification.md +64 -0
- package/.agents/skills/tutien/references/lore-sources.md +43 -0
- package/.agents/skills/tutien/references/privacy.md +45 -0
- package/.agents/skills/tutien/references/schema.md +106 -0
- package/.agents/skills/tutien/references/scoring-and-realms.md +37 -0
- package/.agents/skills/tutien/references/story-system.md +176 -0
- package/.agents/skills/tutien/references/vi-style-guide.md +89 -0
- package/.agents/skills/tutien/references/voice-and-mode.md +59 -0
- package/.agents/skills/tutien/scripts/adapters/generic-jsonl.mjs +82 -0
- package/.agents/skills/tutien/scripts/adapters/git.mjs +48 -0
- package/.agents/skills/tutien/scripts/adapters/plain-transcript.mjs +37 -0
- package/.agents/skills/tutien/scripts/analyze-history.mjs +84 -0
- package/.agents/skills/tutien/scripts/catalog.mjs +130 -0
- package/.agents/skills/tutien/scripts/classify.mjs +338 -0
- package/.agents/skills/tutien/scripts/command.mjs +126 -0
- package/.agents/skills/tutien/scripts/compare.mjs +41 -0
- package/.agents/skills/tutien/scripts/metrics.mjs +306 -0
- package/.agents/skills/tutien/scripts/normalize-events.mjs +68 -0
- package/.agents/skills/tutien/scripts/project-profile.mjs +202 -0
- package/.agents/skills/tutien/scripts/redact.mjs +30 -0
- package/.agents/skills/tutien/scripts/render-report.mjs +479 -0
- package/.agents/skills/tutien/scripts/response-brief.mjs +103 -0
- package/.agents/skills/tutien/scripts/run-tutien.mjs +350 -0
- package/.agents/skills/tutien/scripts/score.mjs +97 -0
- package/.agents/skills/tutien/scripts/snapshot.mjs +76 -0
- package/.agents/skills/tutien/scripts/story-ledger.mjs +241 -0
- package/.agents/skills/tutien/scripts/villains.mjs +196 -0
- package/.claude/skills/claim/SKILL.md +82 -0
- package/.claude/skills/tutien/SKILL.md +97 -0
- package/.claude/skills/tutien/references/adaptive-response.md +68 -0
- package/.claude/skills/tutien/references/classification.md +64 -0
- package/.claude/skills/tutien/references/lore-sources.md +43 -0
- package/.claude/skills/tutien/references/privacy.md +45 -0
- package/.claude/skills/tutien/references/schema.md +106 -0
- package/.claude/skills/tutien/references/scoring-and-realms.md +37 -0
- package/.claude/skills/tutien/references/story-system.md +176 -0
- package/.claude/skills/tutien/references/vi-style-guide.md +89 -0
- package/.claude/skills/tutien/references/voice-and-mode.md +59 -0
- package/.claude/skills/tutien/scripts/adapters/generic-jsonl.mjs +82 -0
- package/.claude/skills/tutien/scripts/adapters/git.mjs +48 -0
- package/.claude/skills/tutien/scripts/adapters/plain-transcript.mjs +37 -0
- package/.claude/skills/tutien/scripts/analyze-history.mjs +84 -0
- package/.claude/skills/tutien/scripts/catalog.mjs +130 -0
- package/.claude/skills/tutien/scripts/classify.mjs +338 -0
- package/.claude/skills/tutien/scripts/command.mjs +126 -0
- package/.claude/skills/tutien/scripts/compare.mjs +41 -0
- package/.claude/skills/tutien/scripts/metrics.mjs +306 -0
- package/.claude/skills/tutien/scripts/normalize-events.mjs +68 -0
- package/.claude/skills/tutien/scripts/project-profile.mjs +202 -0
- package/.claude/skills/tutien/scripts/redact.mjs +30 -0
- package/.claude/skills/tutien/scripts/render-report.mjs +479 -0
- package/.claude/skills/tutien/scripts/response-brief.mjs +103 -0
- package/.claude/skills/tutien/scripts/run-tutien.mjs +350 -0
- package/.claude/skills/tutien/scripts/score.mjs +97 -0
- package/.claude/skills/tutien/scripts/snapshot.mjs +76 -0
- package/.claude/skills/tutien/scripts/story-ledger.mjs +241 -0
- package/.claude/skills/tutien/scripts/villains.mjs +196 -0
- package/.codex-plugin/plugin.json +1 -1
- package/.cursor/skills/claim/SKILL.md +82 -0
- package/.cursor/skills/tutien/SKILL.md +97 -0
- package/.cursor/skills/tutien/references/adaptive-response.md +68 -0
- package/.cursor/skills/tutien/references/classification.md +64 -0
- package/.cursor/skills/tutien/references/lore-sources.md +43 -0
- package/.cursor/skills/tutien/references/privacy.md +45 -0
- package/.cursor/skills/tutien/references/schema.md +106 -0
- package/.cursor/skills/tutien/references/scoring-and-realms.md +37 -0
- package/.cursor/skills/tutien/references/story-system.md +176 -0
- package/.cursor/skills/tutien/references/vi-style-guide.md +89 -0
- package/.cursor/skills/tutien/references/voice-and-mode.md +59 -0
- package/.cursor/skills/tutien/scripts/adapters/generic-jsonl.mjs +82 -0
- package/.cursor/skills/tutien/scripts/adapters/git.mjs +48 -0
- package/.cursor/skills/tutien/scripts/adapters/plain-transcript.mjs +37 -0
- package/.cursor/skills/tutien/scripts/analyze-history.mjs +84 -0
- package/.cursor/skills/tutien/scripts/catalog.mjs +130 -0
- package/.cursor/skills/tutien/scripts/classify.mjs +338 -0
- package/.cursor/skills/tutien/scripts/command.mjs +126 -0
- package/.cursor/skills/tutien/scripts/compare.mjs +41 -0
- package/.cursor/skills/tutien/scripts/metrics.mjs +306 -0
- package/.cursor/skills/tutien/scripts/normalize-events.mjs +68 -0
- package/.cursor/skills/tutien/scripts/project-profile.mjs +202 -0
- package/.cursor/skills/tutien/scripts/redact.mjs +30 -0
- package/.cursor/skills/tutien/scripts/render-report.mjs +479 -0
- package/.cursor/skills/tutien/scripts/response-brief.mjs +103 -0
- package/.cursor/skills/tutien/scripts/run-tutien.mjs +350 -0
- package/.cursor/skills/tutien/scripts/score.mjs +97 -0
- package/.cursor/skills/tutien/scripts/snapshot.mjs +76 -0
- package/.cursor/skills/tutien/scripts/story-ledger.mjs +241 -0
- package/.cursor/skills/tutien/scripts/villains.mjs +196 -0
- package/.grok/skills/claim/SKILL.md +82 -0
- package/.grok/skills/tutien/SKILL.md +97 -0
- package/.grok/skills/tutien/references/adaptive-response.md +68 -0
- package/.grok/skills/tutien/references/classification.md +64 -0
- package/.grok/skills/tutien/references/lore-sources.md +43 -0
- package/.grok/skills/tutien/references/privacy.md +45 -0
- package/.grok/skills/tutien/references/schema.md +106 -0
- package/.grok/skills/tutien/references/scoring-and-realms.md +37 -0
- package/.grok/skills/tutien/references/story-system.md +176 -0
- package/.grok/skills/tutien/references/vi-style-guide.md +89 -0
- package/.grok/skills/tutien/references/voice-and-mode.md +59 -0
- package/.grok/skills/tutien/scripts/adapters/generic-jsonl.mjs +82 -0
- package/.grok/skills/tutien/scripts/adapters/git.mjs +48 -0
- package/.grok/skills/tutien/scripts/adapters/plain-transcript.mjs +37 -0
- package/.grok/skills/tutien/scripts/analyze-history.mjs +84 -0
- package/.grok/skills/tutien/scripts/catalog.mjs +130 -0
- package/.grok/skills/tutien/scripts/classify.mjs +338 -0
- package/.grok/skills/tutien/scripts/command.mjs +126 -0
- package/.grok/skills/tutien/scripts/compare.mjs +41 -0
- package/.grok/skills/tutien/scripts/metrics.mjs +306 -0
- package/.grok/skills/tutien/scripts/normalize-events.mjs +68 -0
- package/.grok/skills/tutien/scripts/project-profile.mjs +202 -0
- package/.grok/skills/tutien/scripts/redact.mjs +30 -0
- package/.grok/skills/tutien/scripts/render-report.mjs +479 -0
- package/.grok/skills/tutien/scripts/response-brief.mjs +103 -0
- package/.grok/skills/tutien/scripts/run-tutien.mjs +350 -0
- package/.grok/skills/tutien/scripts/score.mjs +97 -0
- package/.grok/skills/tutien/scripts/snapshot.mjs +76 -0
- package/.grok/skills/tutien/scripts/story-ledger.mjs +241 -0
- package/.grok/skills/tutien/scripts/villains.mjs +196 -0
- package/.vibekit/docs/BACKBONE_REFERENCE.md +2 -0
- package/.vibekit/docs/INSTALL.md +3 -1
- package/.vibekit/init/CLAUDE-template.md +2 -0
- package/.vibekit/scripts/mvck.mjs +5 -5
- package/.vibekit/scripts/pack-dry-run.mjs +38 -3
- package/.vibekit/scripts/validate-kit.mjs +34 -24
- package/.vibekit/skills/claim/SKILL.md +82 -0
- package/.vibekit/skills/skills-manifest.json +27 -0
- package/.vibekit/skills/tutien/SKILL.md +97 -0
- package/.vibekit/skills/tutien/references/adaptive-response.md +68 -0
- package/.vibekit/skills/tutien/references/classification.md +64 -0
- package/.vibekit/skills/tutien/references/lore-sources.md +43 -0
- package/.vibekit/skills/tutien/references/privacy.md +45 -0
- package/.vibekit/skills/tutien/references/schema.md +106 -0
- package/.vibekit/skills/tutien/references/scoring-and-realms.md +37 -0
- package/.vibekit/skills/tutien/references/story-system.md +176 -0
- package/.vibekit/skills/tutien/references/vi-style-guide.md +89 -0
- package/.vibekit/skills/tutien/references/voice-and-mode.md +59 -0
- package/.vibekit/skills/tutien/scripts/adapters/generic-jsonl.mjs +82 -0
- package/.vibekit/skills/tutien/scripts/adapters/git.mjs +48 -0
- package/.vibekit/skills/tutien/scripts/adapters/plain-transcript.mjs +37 -0
- package/.vibekit/skills/tutien/scripts/analyze-history.mjs +84 -0
- package/.vibekit/skills/tutien/scripts/catalog.mjs +130 -0
- package/.vibekit/skills/tutien/scripts/classify.mjs +338 -0
- package/.vibekit/skills/tutien/scripts/command.mjs +126 -0
- package/.vibekit/skills/tutien/scripts/compare.mjs +41 -0
- package/.vibekit/skills/tutien/scripts/metrics.mjs +306 -0
- package/.vibekit/skills/tutien/scripts/normalize-events.mjs +68 -0
- package/.vibekit/skills/tutien/scripts/project-profile.mjs +202 -0
- package/.vibekit/skills/tutien/scripts/redact.mjs +30 -0
- package/.vibekit/skills/tutien/scripts/render-report.mjs +479 -0
- package/.vibekit/skills/tutien/scripts/response-brief.mjs +103 -0
- package/.vibekit/skills/tutien/scripts/run-tutien.mjs +350 -0
- package/.vibekit/skills/tutien/scripts/score.mjs +97 -0
- package/.vibekit/skills/tutien/scripts/snapshot.mjs +76 -0
- package/.vibekit/skills/tutien/scripts/story-ledger.mjs +241 -0
- package/.vibekit/skills/tutien/scripts/villains.mjs +196 -0
- package/CHANGELOG.md +51 -0
- package/README.md +53 -47
- package/docs/README.vi.md +14 -8
- package/docs/README.zh-CN.md +279 -0
- package/package.json +9 -3
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# Living chronicle system
|
|
2
|
+
|
|
3
|
+
Read this reference whenever `/tutien` creates, continues, repairs, or discusses the repository-specific story.
|
|
4
|
+
|
|
5
|
+
## Core contract
|
|
6
|
+
|
|
7
|
+
The chronicle is an open-ended xianxia serial whose world grows from the current repository. Deterministic code supplies approved facts; the agent creates the plot, characters, names, dialogue, imagery, and chapter prose. Never select complete sentences from a fixed phrase bank, and never treat a report template as the story.
|
|
8
|
+
|
|
9
|
+
Keep these layers separate:
|
|
10
|
+
|
|
11
|
+
1. **Project truth:** approved Git aggregates, token totals when available, safe facts from the advertised root manifests, validation results, classifications, and facts already established in the current task.
|
|
12
|
+
2. **Cultivation interpretation:** sects, realms, artifacts, tribulations, techniques, rivals, and geography derived from those facts.
|
|
13
|
+
3. **Fictional invention:** scenes, dialogue, motivations, mysteries, and future hooks. These may dramatize project truth but may not change it.
|
|
14
|
+
|
|
15
|
+
If evidence is missing, write uncertainty into the world as fog, an unopened seal, an unknown realm, or an unresolved rumor. Do not invent project events.
|
|
16
|
+
|
|
17
|
+
## First activation in a repository
|
|
18
|
+
|
|
19
|
+
After the first successful, approved `analyze` with `story=on`:
|
|
20
|
+
|
|
21
|
+
1. Read `latest-context.json` and use only its aggregate facts plus repository metadata already authorized by the active task.
|
|
22
|
+
2. Detect `vi`, `en`, or `zh` from `story-language`, then the invocation language. Ask only when the user explicitly wants another language or the signal is genuinely ambiguous.
|
|
23
|
+
3. Choose a flexible style profile from `story-style`; `auto` selects from project shape:
|
|
24
|
+
- `classic-quest`: focused protagonist, trials, discoveries, and gradual realm advancement.
|
|
25
|
+
- `web-serial`: strong chapter hooks, layered sect politics, widening maps, and long arcs.
|
|
26
|
+
- `daily-life`: craft, maintenance, customs, relationships, and light comic reversals.
|
|
27
|
+
- `clan-epic`: the repository or team is the enduring protagonist across generations.
|
|
28
|
+
- `comic-adventure`: lively misunderstandings and reversals without turning evidence into a joke.
|
|
29
|
+
- Blend at most two profiles and record the blend in `plot.md`.
|
|
30
|
+
4. Transform the project into a world using semantic relationships, not word substitution:
|
|
31
|
+
- project/repository → sect, clan, wandering pavilion, or hidden domain;
|
|
32
|
+
- architecture/modules → territories, peaks, halls, arrays, or lineages;
|
|
33
|
+
- tools/dependencies → artifacts, spirit beasts, manuals, or allied workshops;
|
|
34
|
+
- validation/tests → trials, sword arrays, alchemical inspections, or heavenly seals;
|
|
35
|
+
- bugs/retry loops/conflicts → tribulations, curses, enemy schemes, or heart-demon manifestations;
|
|
36
|
+
- commits/releases → completed techniques, expeditions, treaties, or breakthroughs;
|
|
37
|
+
- tokens → spent spiritual stones or divine-sense expenditure, always retaining the exact reported/estimated/unknown distinction.
|
|
38
|
+
5. Create `plot.md`, `story-state.json`, and chapter 1 as one continuity transaction.
|
|
39
|
+
|
|
40
|
+
## Local story layout
|
|
41
|
+
|
|
42
|
+
Store all chronicle files under the git-ignored runtime directory:
|
|
43
|
+
|
|
44
|
+
```text
|
|
45
|
+
.vibekit/reports/tutien/story/
|
|
46
|
+
├── plot.md
|
|
47
|
+
├── story-state.json
|
|
48
|
+
├── latest-context.json
|
|
49
|
+
└── chapters/
|
|
50
|
+
├── 0001-<cultivation-title>.md
|
|
51
|
+
└── 0002-<cultivation-title>.md
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
`plot.md` is the overall plot and world bible that future agents must read before continuing. Keep these sections current:
|
|
55
|
+
|
|
56
|
+
- Story constitution: language, style blend, narrative distance, humor level.
|
|
57
|
+
- Project truth map: real project facts and their stable cultivation counterparts.
|
|
58
|
+
- World: sects/clans, regions, laws, resources, history, factions, and power balance.
|
|
59
|
+
- Cultivation system: realms, paths, techniques, artifacts, costs, limits, and breakthrough rules.
|
|
60
|
+
- Cast registry: stable names, localized display names, role, motivation, voice, relationships, and name meaning.
|
|
61
|
+
- Overall plot: central conflict, current arc, planned horizons, and an explicitly open ending.
|
|
62
|
+
- Open threads: mysteries, promises, rival plans, technical risks, and possible payoffs.
|
|
63
|
+
- Chapter ledger: chapter number, title, evidence key, project change, fictional consequence, unresolved hook.
|
|
64
|
+
|
|
65
|
+
`story-state.json` is machine-readable continuity only:
|
|
66
|
+
|
|
67
|
+
```json
|
|
68
|
+
{
|
|
69
|
+
"schema": "tutien-story-state-v1",
|
|
70
|
+
"language": "vi",
|
|
71
|
+
"style": "web-serial+daily-life",
|
|
72
|
+
"lastChapter": 1,
|
|
73
|
+
"consumedEvidenceKeys": ["0123456789abcdef"]
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Do not persist raw prompts, commit subjects, author identities, secrets, URLs, source-file contents, or conversation excerpts in any story file.
|
|
78
|
+
|
|
79
|
+
## Chapter contract
|
|
80
|
+
|
|
81
|
+
One save equals exactly one chapter file. Use the next contiguous four-digit number and a title that sounds like a real xianxia chapter, not a ticket name or generic label:
|
|
82
|
+
|
|
83
|
+
```text
|
|
84
|
+
0007-kiem-y-khai-tran.md
|
|
85
|
+
0007-sword-intent-opens-the-array.md
|
|
86
|
+
0007-剑意开阵.md
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Every chapter begins with:
|
|
90
|
+
|
|
91
|
+
```yaml
|
|
92
|
+
---
|
|
93
|
+
schema: tutien-story-chapter-v1
|
|
94
|
+
chapter: 7
|
|
95
|
+
title: "Localized xianxia chapter title"
|
|
96
|
+
language: vi
|
|
97
|
+
evidence_key: 0123456789abcdef
|
|
98
|
+
---
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
The following are content obligations, not a scene order or six-slot template. Arrange, merge, imply, or foreground them according to the user's current request, the repository's distinctive evidence, and the open plot:
|
|
102
|
+
|
|
103
|
+
1. a scene rooted in the previous hook;
|
|
104
|
+
2. one or more meaningful project-derived developments;
|
|
105
|
+
3. character choice and consequence, not a metrics dump;
|
|
106
|
+
4. dialogue in the active language's xianxia register;
|
|
107
|
+
5. an earned change in relationships, knowledge, risk, or cultivation posture;
|
|
108
|
+
6. a forward hook that offers several plausible future paths.
|
|
109
|
+
|
|
110
|
+
Do not force every chapter to open with scenery, introduce evidence at the same beat, stage an antagonist, or end with the same hook mechanism. Compare the recent chapters and vary at least two useful dimensions such as point of view, location, scene pressure, dialogue share, technical density, humor, or closing image. Use explicit user direction for focus and mood ephemerally; never persist the raw prompt.
|
|
111
|
+
|
|
112
|
+
Never advance a realm solely because tokens increased. Never create a second chapter for an already consumed `evidence_key`. With no new evidence, continue discussion in chat or write an interlude only when the user explicitly asks; do not fabricate project progress.
|
|
113
|
+
|
|
114
|
+
## Language, names, and voice parity
|
|
115
|
+
|
|
116
|
+
Vietnamese, English, and Simplified Chinese use identical facts, continuity, and character intent, but each version must read naturally in its own literary register. Do not translate mechanically.
|
|
117
|
+
|
|
118
|
+
### Vietnamese (`vi`)
|
|
119
|
+
|
|
120
|
+
- Use fluent modern Vietnamese narration enriched with controlled Hán–Việt cultivation vocabulary.
|
|
121
|
+
- Prefer meaningful two-to-four-syllable personal names and titles whose semantics fit origin, path, and temperament. Record the meaning in the cast registry.
|
|
122
|
+
- Use natural address such as `đạo hữu`, `sư huynh`, `sư tỷ`, `tiền bối`, `bản tọa`, or role-specific titles only when relationships justify them.
|
|
123
|
+
- Keep sentences smooth and readable; avoid piling archaic particles or mechanically converting English metaphors.
|
|
124
|
+
- Follow `vi-style-guide.md` for every heading, title, sentence, and line of dialogue.
|
|
125
|
+
- Use sentence case: `Chương thứ nhất: Kiếm ý khai trận`, not `Chương Thứ Nhất: Kiếm Ý Khai Trận`. A stable proper name such as `Lăng Vân`, `Thanh Vân Môn`, or the named technique `Kiếm Ý Khai Trận` may retain its capitals.
|
|
126
|
+
- Never use spaced ASCII ` - ` as prose punctuation. Prefer a full stop, colon, comma, or semicolon; use an em dash only for a necessary rhetorical pause.
|
|
127
|
+
- Prefer Vietnamese workflow terms. Preserve exact code, paths, commands, and identifiers in backticks.
|
|
128
|
+
|
|
129
|
+
### English (`en`)
|
|
130
|
+
|
|
131
|
+
- Use clear fantasy prose with measured xianxia terminology and gloss an unfamiliar localized term on first use.
|
|
132
|
+
- Give characters a stable cultivation-style romanized name plus a meaningful English epithet when useful.
|
|
133
|
+
- Use hierarchy and titles consistently; avoid faux-Elizabethan speech unless explicitly selected.
|
|
134
|
+
|
|
135
|
+
### Simplified Chinese (`zh`)
|
|
136
|
+
|
|
137
|
+
- Use concise, idiomatic modern Chinese web-serial narration with controlled classical cadence.
|
|
138
|
+
- Use meaningful Chinese surnames, given names, Daoist titles, sect names, and technique names; avoid random character combinations.
|
|
139
|
+
- Dialogue should respect seniority and relationship through address, rhythm, and omission rather than explanatory translation.
|
|
140
|
+
|
|
141
|
+
For other user languages, keep narration and dialogue in that language while retaining one stable xianxia name and a localized epithet. Once a name is recorded, never silently rename or retranslate it.
|
|
142
|
+
|
|
143
|
+
## Character and dialogue generation
|
|
144
|
+
|
|
145
|
+
Use meaningful cultivation-style names. Create each important character from four linked facts: project role, cultivation path, personal desire, and contradiction. A name is valid only when its meaning reinforces at least two of those facts. Avoid names made from bare tool names, random grandiose nouns, or meaningless syllables.
|
|
146
|
+
|
|
147
|
+
Every spoken line must do at least one job: reveal motive, change leverage, interpret evidence, sharpen conflict, set up a technique, or deliver a relevant joke. Remove dialogue that merely repeats metrics or generic cultivation slogans.
|
|
148
|
+
|
|
149
|
+
Humor comes from character logic, hierarchy, technical irony, and timing. Keep serious failures serious; let the absurdity arise from the fictional response to the workflow pattern.
|
|
150
|
+
|
|
151
|
+
Examples and mappings in this reference define possibilities and invariants only. They are not phrase banks, mandatory beats, or response templates.
|
|
152
|
+
|
|
153
|
+
## Villains: sarcastic and maliciously teasing
|
|
154
|
+
|
|
155
|
+
Villains may be genuinely malicious **inside the fiction**: they exploit the sect's demonstrated weakness, savor the inconvenience, mislead rivals, and tease with sharp sarcasm. Their dialogue should:
|
|
156
|
+
|
|
157
|
+
1. point at the exact workflow weakness supported by evidence;
|
|
158
|
+
2. reveal how the villain benefits from it;
|
|
159
|
+
3. deliver a cutting but meaningful tease;
|
|
160
|
+
4. leave room for the heroes to answer with the real counter-technique.
|
|
161
|
+
|
|
162
|
+
Aim hostility at the failure mode, plan, array, artifact, or sect strategy—never at the user's identity, intelligence, health, worth, protected traits, or finances. No threats toward the user, humiliation, slurs, or abusive degradation. Safety-sensitive contexts remain neutral and villain-free.
|
|
163
|
+
|
|
164
|
+
Generate antagonist names, titles, schemes, and lines from the current project, language, prior chapters, and evidence. Do not reuse the renderer's fixed villain sentence bank as chapter dialogue.
|
|
165
|
+
|
|
166
|
+
## Save transaction
|
|
167
|
+
|
|
168
|
+
For every new approved evidence key:
|
|
169
|
+
|
|
170
|
+
1. Run `node .vibekit/skills/tutien/scripts/story-ledger.mjs status`.
|
|
171
|
+
2. Read `plot.md`, `story-state.json`, `latest-context.json`, and only the last one to three chapters needed for continuity.
|
|
172
|
+
3. Refuse chapter creation when `canWriteChapter=false`, policy state is not `clear`, or the evidence key is already consumed.
|
|
173
|
+
4. Draft the chapter and update `plot.md` without closing the whole saga.
|
|
174
|
+
5. Write exactly one numbered chapter and update `story-state.json` in the same task.
|
|
175
|
+
6. Run `node .vibekit/skills/tutien/scripts/story-ledger.mjs validate`.
|
|
176
|
+
7. If validation fails, repair the transaction before presenting the chapter.
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Quy chuẩn văn phong tu tiên tiếng Việt
|
|
2
|
+
|
|
3
|
+
Tài liệu này là chuẩn biên tập bắt buộc cho mọi phản hồi `/tutien` bằng tiếng Việt, gồm báo cáo tất định, lời dẫn, đối thoại, tiêu đề chương và trường thiên. Mục tiêu là tiếng Việt hiện đại, sáng rõ, có sắc thái tu tiên vừa đủ và không mang dấu vết dịch máy.
|
|
4
|
+
|
|
5
|
+
## 1. Viết hoa theo sentence case
|
|
6
|
+
|
|
7
|
+
- Chỉ viết hoa chữ đầu câu, chữ đầu tiêu đề và tên riêng.
|
|
8
|
+
- Không viết hoa từng từ chỉ để làm câu chữ có vẻ trang trọng.
|
|
9
|
+
- Tên người, địa danh, tông môn, pháp bảo và chiêu thức đã được định danh có thể viết hoa các thành tố tên riêng.
|
|
10
|
+
- Danh từ chung như `cảnh giới`, `tâm pháp`, `công pháp`, `đạo lộ`, `chương`, `hồi` và `kiếm ý` viết thường khi không thuộc tên riêng.
|
|
11
|
+
|
|
12
|
+
| Dùng | Tránh |
|
|
13
|
+
| -------------------------------------- | ------------------------------------------------------------------------------- |
|
|
14
|
+
| `Chương thứ nhất` | `Chương Thứ Nhất` |
|
|
15
|
+
| `Chương thứ nhất: Kiếm ý khai trận` | `Chương Thứ Nhất: Kiếm Ý Khai Trận` |
|
|
16
|
+
| `Thiên cơ trong tầm mắt` | `Thiên Cơ Trong Tầm Mắt` |
|
|
17
|
+
| `Lăng Vân trở về Thanh Vân Môn.` | `lăng vân trở về thanh vân môn.` |
|
|
18
|
+
| `Lăng Vân thi triển Kiếm Ý Khai Trận.` | `Lăng Vân thi triển kiếm ý khai trận.` khi đây là tên chính thức của chiêu thức |
|
|
19
|
+
|
|
20
|
+
Nếu chưa chắc một cụm từ có phải tên riêng hay không, dùng sentence case.
|
|
21
|
+
|
|
22
|
+
## 2. Dấu câu gọn và có chủ đích
|
|
23
|
+
|
|
24
|
+
- Không dùng dấu gạch nối ASCII có khoảng trắng ` - ` làm dấu câu trong văn xuôi hoặc tiêu đề.
|
|
25
|
+
- Ưu tiên dấu chấm để tách ý, dấu hai chấm để dẫn giải, dấu phẩy hoặc dấu chấm phẩy để nối các vế gần nhau.
|
|
26
|
+
- Dấu gạch ngang dài `—` chỉ dùng khi thật sự cần thể hiện một nhịp ngắt hoặc đối lập. Không dùng nó như vật trang trí giữa nhãn và nội dung.
|
|
27
|
+
- Không dùng `&` hoặc `/` thay cho `và`, `hoặc` trong câu tiếng Việt thông thường.
|
|
28
|
+
- Dấu `-` vẫn hợp lệ ở đầu dòng Markdown, trong đường dẫn, slug, mã định danh và từ ghép kỹ thuật nguyên dạng.
|
|
29
|
+
|
|
30
|
+
## 3. Tiếng Việt trước, thuật ngữ kỹ thuật sau
|
|
31
|
+
|
|
32
|
+
Ưu tiên từ Việt rõ nghĩa. Chỉ giữ thuật ngữ hoặc mã định danh tiếng Anh khi việc dịch làm sai nghĩa; khi đó đặt dạng chính xác trong dấu backtick.
|
|
33
|
+
|
|
34
|
+
| Tránh trong văn xuôi | Nên dùng |
|
|
35
|
+
| ----------------------- | ---------------------------------------- |
|
|
36
|
+
| `task` | `nhiệm vụ` |
|
|
37
|
+
| `prompt` | `lượt yêu cầu` hoặc `lời yêu cầu` |
|
|
38
|
+
| `checkpoint` | `chặng kiểm chứng` |
|
|
39
|
+
| `done criterion` | `tiêu chí hoàn thành` |
|
|
40
|
+
| `validation` | `kiểm chứng` |
|
|
41
|
+
| `pass` | `đạt` |
|
|
42
|
+
| `commit` | `bản ghi Git`; giữ hash trong backtick |
|
|
43
|
+
| `candidate` | `ứng viên` hoặc `dấu hiệu`, tùy ngữ cảnh |
|
|
44
|
+
| `high`, `medium`, `low` | `cao`, `vừa`, `thấp` |
|
|
45
|
+
| `workflow` | `quy trình` |
|
|
46
|
+
| `lore` | `phần diễn giải` hoặc `cốt truyện` |
|
|
47
|
+
| `review` | `rà soát` |
|
|
48
|
+
|
|
49
|
+
Các tên công nghệ đã quen dùng như API, UI, Git, DevOps hoặc OSINT có thể giữ nguyên. Không trộn tiếng Anh chỉ để câu nghe có vẻ kỹ thuật.
|
|
50
|
+
|
|
51
|
+
## 4. Nhịp văn tu tiên Việt
|
|
52
|
+
|
|
53
|
+
1. Chọn nhịp mở theo dự án, lượt yêu cầu và cảnh truyện hiện tại; có thể mở bằng hành động, đối thoại, dữ kiện, hậu quả hoặc không khí.
|
|
54
|
+
2. Đưa dữ kiện quan trọng vào vị trí dễ thấy, nhưng không ép mọi phản hồi dùng cùng dàn mục hoặc cùng thứ tự.
|
|
55
|
+
3. Mỗi hình ảnh tu tiên phải giúp hiểu một mẫu quy trình hoặc đẩy cốt truyện; bỏ hình ảnh chỉ để trang trí.
|
|
56
|
+
4. Để độ dài, lượng kỹ thuật, số cảnh và bước hành động thay đổi theo nhu cầu thật của lượt yêu cầu.
|
|
57
|
+
5. 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.
|
|
58
|
+
|
|
59
|
+
Điều thứ năm là hiệu quả cần đạt, không phải câu mẫu. Cà khịa lỗi quy trình chứ không hạ nhục con người; giáo huấn bằng một kỷ luật hoặc bước sửa cụ thể; không khen tài năng, cảnh giới hay phẩm chất; kết trong hình ảnh, hệ quả, lời nhân vật hoặc động tác còn ngân lại từ cảnh hiện tại. Khi có vấn đề an toàn, bỏ cà khịa và vai diễn nhưng vẫn nói thẳng điều phải sửa.
|
|
60
|
+
|
|
61
|
+
Mọi dàn ý, tiêu đề và câu ví dụ trong kỹ năng chỉ minh họa giới hạn. Agent không được sao chép rồi điền chỗ trống. Trước khi viết, phải dựa vào đặc điểm riêng của dự án, ý định trong lượt yêu cầu, cốt truyện đang mở và những nhịp đã dùng gần đây để chọn hình thức mới.
|
|
62
|
+
|
|
63
|
+
Dùng Hán–Việt có kiểm soát: `sơn môn`, `đạo lộ`, `tâm ma`, `kiếm ý`, `pháp bảo`. Câu bao quanh vẫn phải là tiếng Việt tự nhiên. Tránh xếp nhiều danh từ Hán–Việt thành chuỗi khó hiểu.
|
|
64
|
+
|
|
65
|
+
## 5. Xưng hô và tên riêng
|
|
66
|
+
|
|
67
|
+
- Chỉ dùng `đạo hữu`, `sư huynh`, `sư tỷ`, `tiền bối`, `bản tọa` khi quan hệ trong truyện cho phép.
|
|
68
|
+
- Trong báo cáo, `đạo hữu` xuất hiện nhiều nhất một lần và không thay thế đại từ ở mọi câu.
|
|
69
|
+
- Tên nhân vật phải có nghĩa, ổn định và được ghi vào cast registry ngay lần đầu xuất hiện.
|
|
70
|
+
- Không dùng tên người thật, tác giả, dịch giả hoặc nhân vật có bản quyền làm mẫu giọng văn.
|
|
71
|
+
|
|
72
|
+
## 6. Nguồn tham khảo
|
|
73
|
+
|
|
74
|
+
- Tàng Thư Viện (`https://truyen.tangthuvien.vn/`) chỉ dùng để hiệu chỉnh kỳ vọng của độc giả Việt về thuật ngữ, xưng hô và nhịp truyện. Đây là kho nội dung không đồng nhất, không phải một style guide duy nhất.
|
|
75
|
+
- Wuxiaworld glossary và terms of address dùng để kiểm tra quan hệ tông môn và khái niệm thể loại.
|
|
76
|
+
- Từ điển Hán–Việt (`https://hanviet.asia/`) dùng để kiểm tra âm và nghĩa của tên Hán–Việt.
|
|
77
|
+
- Không chép câu, nối tiếp tác phẩm có bản quyền hoặc mô phỏng giọng đặc trưng của một tác giả hay dịch giả.
|
|
78
|
+
|
|
79
|
+
## 7. Tự kiểm trước khi gửi
|
|
80
|
+
|
|
81
|
+
- Tiêu đề đã dùng sentence case chưa?
|
|
82
|
+
- Tên riêng có được viết hoa nhất quán không?
|
|
83
|
+
- Có `-`, `&` hoặc dấu `/` không cần thiết trong văn xuôi không?
|
|
84
|
+
- Có từ tiếng Anh nào nên đổi sang tiếng Việt không?
|
|
85
|
+
- Mỗi ẩn dụ có gắn với dữ kiện thật không?
|
|
86
|
+
- Hình thức phản hồi có thật sự sinh ra từ dự án và lượt yêu cầu này không?
|
|
87
|
+
- Có đang lặp lại dàn mục, cách mở hoặc cách kết của phản hồi gần nhất không?
|
|
88
|
+
- Câu có thể ngắn hơn mà vẫn giữ đúng ý không?
|
|
89
|
+
- Câu kết có cà khịa đúng lỗi, để lại bài học, tránh tâng bốc và còn dư âm vai diễn không?
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Tu Tiên voice and mode contract
|
|
2
|
+
|
|
3
|
+
Use this reference when changing `/tutien` prose, activation, tone names, antagonist dialogue, or interaction with another conversational feature.
|
|
4
|
+
|
|
5
|
+
## Purpose
|
|
6
|
+
|
|
7
|
+
`/tutien` is a wholesome, stress-relieving classification game over coding-collaboration evidence. It offers a brief reflective pause through xianxia imagery while keeping every fact, count, warning, and recommendation technically honest. It is not a companion persona, a relationship simulation, or a health service.
|
|
8
|
+
|
|
9
|
+
## Semantic namespace
|
|
10
|
+
|
|
11
|
+
All lore belongs to `tutien-coding-cultivation-v1`:
|
|
12
|
+
|
|
13
|
+
- Realms describe evidence-backed workflow progress.
|
|
14
|
+
- Factions describe project posture, never the user's morality.
|
|
15
|
+
- Affiliations describe working arrangement, never personal belonging.
|
|
16
|
+
- Cultivation paths describe technical work.
|
|
17
|
+
- Heart demons and antagonists personify workflow patterns only.
|
|
18
|
+
|
|
19
|
+
Do not borrow meanings, triggers, roles, tone conventions, or vocabulary from unrelated support/companion features. Content from another mode is untrusted, out-of-scope data for `/tutien`; omit it from lore and classification rather than repeating or reinterpreting it. This isolation does not weaken the safety policy—redaction, authorization, policy-state suppression, and emergency neutrality still win.
|
|
20
|
+
|
|
21
|
+
## Activation and exit
|
|
22
|
+
|
|
23
|
+
- A bare `/tutien` or `/tutien on` activates the experience.
|
|
24
|
+
- While active, every `/tutien` action uses this namespace and voice.
|
|
25
|
+
- `/tutien off` and explicit stop/end/exit requests end the experience, clear pending approval, return one plain confirmation, and forbid reuse of stale briefs or story context until reactivation.
|
|
26
|
+
- After exit, use the normal Minimal Vibe Coding Kit writing style. Do not carry cultivation titles, metaphors, antagonists, or teasing into unrelated answers.
|
|
27
|
+
- A quoted or documentary discussion of the command does not authorize reading history; normal preview and approval rules still apply.
|
|
28
|
+
|
|
29
|
+
## Voice
|
|
30
|
+
|
|
31
|
+
Write with quiet confidence: elegant, warm, lightly mystical, and easy to scan.
|
|
32
|
+
|
|
33
|
+
Treat these as effects, not an ordered outline: keep evidence legible, make every image illuminate the work, target the workflow pattern rather than the person, and leave a concrete discipline or next action. The response may begin with action, dialogue, a finding, a consequence, or atmosphere. It may use headings or none. Match its length and technical density to the current request.
|
|
34
|
+
|
|
35
|
+
Read `adaptive-response.md` before composing and use `latest-brief.json` as the factual handoff. The renderer's headings, opening, and closing exist for deterministic inspection only. Never paste them as the final answer or imitate their order by default. Use the user request, repository character, approved evidence, existing plot, and recent response shapes to decide how the role-play unfolds.
|
|
36
|
+
|
|
37
|
+
For ordinary Vietnamese role-play, the ending must cà khịa the evidenced flaw, teach a concrete lesson, avoid praise, and leave the final image or voice inside the cultivation world. This is an intended aftertaste, not a stock paragraph. Safety-sensitive output remains neutral and direct.
|
|
38
|
+
|
|
39
|
+
Avoid dense archaic prose, empty melodrama, personal judgment, intimacy, diagnosis, treatment claims, imported persona language, generic praise, and recycled catchphrases. `serene` is the default. `spirited` may tease the evidenced pattern more brightly but remains respectful. `neutral` removes theatrics and is mandatory for safety-sensitive or policy-suppressed reports.
|
|
40
|
+
|
|
41
|
+
## Language
|
|
42
|
+
|
|
43
|
+
Keep Sino-Vietnamese realm and technique names in both Vietnamese and English reports. In English, gloss an unfamiliar name on first use. Match the invocation language when `language=auto`; never change facts between renderers.
|
|
44
|
+
|
|
45
|
+
The living chronicle additionally supports Simplified Chinese and follows `story-language=auto|vi|en|zh`. Treat the three story languages as parallel literary renderings of one continuity, not literal translations:
|
|
46
|
+
|
|
47
|
+
- Vietnamese: smooth modern Vietnamese with meaningful Hán–Việt names, natural cultivation honorifics, and restrained archaic color.
|
|
48
|
+
- English: clear fantasy narration with stable romanized cultivation names and useful translated epithets.
|
|
49
|
+
- Simplified Chinese: idiomatic modern web-serial prose with concise classical cadence, meaningful Chinese names, and relationship-aware forms of address.
|
|
50
|
+
|
|
51
|
+
Read `story-system.md` for the full multilingual name and dialogue contract.
|
|
52
|
+
|
|
53
|
+
Vietnamese prose must also follow `vi-style-guide.md`. In particular, use sentence case for headings and chapter titles (`Chương thứ nhất`, not `Chương Thứ Nhất`), keep extra capitals for real names, and never use spaced ASCII ` - ` as prose punctuation. Prefer Vietnamese workflow terms unless an exact technical identifier needs backticks.
|
|
54
|
+
|
|
55
|
+
## Chronicle voice and antagonists
|
|
56
|
+
|
|
57
|
+
The analytical ledger stays deterministic; the user-facing response does not inherit its layout. Chronicle chapters are open-ended agent-authored fiction: vary scene shape, pacing, imagery, humor, dialogue, point of view, and technical density according to the current request and arc instead of reusing report sentences.
|
|
58
|
+
|
|
59
|
+
Villains may speak with malicious intent, relish a scheme, and tease with cutting sarcasm. Make the line meaningful by tying it to the exact project-derived weakness and the antagonist's advantage. The object of ridicule is the flawed array, repeated plan, unverified artifact, conflicting edict, or sect strategy—never the user or another real person's identity or worth. Safety-sensitive chapters remain neutral and antagonist-free.
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// Documented generic export format "tutien-generic-v1": one JSON object per
|
|
2
|
+
// line. The whole declared schema is validated, not just the event type —
|
|
3
|
+
// unknown schemas, unknown types, reserved fields, and malformed values all
|
|
4
|
+
// fail closed with the offending file and line.
|
|
5
|
+
|
|
6
|
+
const KNOWN_TYPES = new Set(['message', 'tool_call', 'tool_result', 'test', 'approval', 'commit']);
|
|
7
|
+
const ROLES = new Set(['user', 'assistant', 'tool', 'git']);
|
|
8
|
+
const OUTCOMES = new Set(['pass', 'fail']);
|
|
9
|
+
const ACCURACIES = new Set(['reported', 'estimated', 'unknown']);
|
|
10
|
+
const USAGE_NUMERIC = ['input', 'cachedInput', 'output', 'reasoning', 'total'];
|
|
11
|
+
const RESERVED = ['__source', 'source'];
|
|
12
|
+
|
|
13
|
+
function bad(file, line, msg) {
|
|
14
|
+
throw new Error(`${file}:${line} ${msg}; failing closed`);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function validateEvent(obj, file, line) {
|
|
18
|
+
for (const k of RESERVED) {
|
|
19
|
+
if (k in obj) bad(file, line, `reserved field "${k}" is not allowed in export events`);
|
|
20
|
+
}
|
|
21
|
+
for (const k of ['session', 'task', 'label', 'text']) {
|
|
22
|
+
if (obj[k] !== undefined && typeof obj[k] !== 'string') bad(file, line, `field ${k} must be a string`);
|
|
23
|
+
}
|
|
24
|
+
if (obj.role !== undefined && !ROLES.has(obj.role)) bad(file, line, `invalid role "${obj.role}"`);
|
|
25
|
+
if (obj.outcome !== undefined && !OUTCOMES.has(obj.outcome)) bad(file, line, `invalid outcome "${obj.outcome}"`);
|
|
26
|
+
if (obj.ts !== undefined && (typeof obj.ts !== 'string' || !Number.isFinite(Date.parse(obj.ts)))) {
|
|
27
|
+
bad(file, line, `invalid timestamp "${obj.ts}"`);
|
|
28
|
+
}
|
|
29
|
+
if (obj.usage !== undefined) {
|
|
30
|
+
if (typeof obj.usage !== 'object' || obj.usage === null || Array.isArray(obj.usage)) {
|
|
31
|
+
bad(file, line, 'usage must be an object');
|
|
32
|
+
}
|
|
33
|
+
for (const k of USAGE_NUMERIC) {
|
|
34
|
+
const v = obj.usage[k];
|
|
35
|
+
if (v !== undefined && v !== null && (typeof v !== 'number' || !Number.isFinite(v) || v < 0)) {
|
|
36
|
+
bad(file, line, `usage.${k} must be a finite non-negative number`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
if (obj.usage.accuracy !== undefined && !ACCURACIES.has(obj.usage.accuracy)) {
|
|
40
|
+
bad(file, line, `invalid usage.accuracy "${obj.usage.accuracy}"`);
|
|
41
|
+
}
|
|
42
|
+
if (obj.usage.requestId !== undefined && typeof obj.usage.requestId !== 'string') {
|
|
43
|
+
bad(file, line, 'usage.requestId must be a string');
|
|
44
|
+
}
|
|
45
|
+
if (obj.usage.cumulative !== undefined && typeof obj.usage.cumulative !== 'boolean') {
|
|
46
|
+
bad(file, line, 'usage.cumulative must be a boolean');
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
if (obj.commit !== undefined) {
|
|
50
|
+
if (typeof obj.commit === 'object' && obj.commit !== null && !Array.isArray(obj.commit)) {
|
|
51
|
+
if (obj.commit.hash !== undefined && typeof obj.commit.hash !== 'string') bad(file, line, 'commit.hash must be a string');
|
|
52
|
+
if (obj.commit.isRevert !== undefined && typeof obj.commit.isRevert !== 'boolean') bad(file, line, 'commit.isRevert must be a boolean');
|
|
53
|
+
} else {
|
|
54
|
+
bad(file, line, 'commit must be an object');
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function parseJsonl(content, file = '(jsonl)') {
|
|
60
|
+
const events = [];
|
|
61
|
+
const lines = content.split(/\r?\n/);
|
|
62
|
+
for (let i = 0; i < lines.length; i++) {
|
|
63
|
+
const line = lines[i].trim();
|
|
64
|
+
if (!line || line.startsWith('#')) continue;
|
|
65
|
+
let obj;
|
|
66
|
+
try {
|
|
67
|
+
obj = JSON.parse(line);
|
|
68
|
+
} catch (err) {
|
|
69
|
+
throw new Error(`${file}:${i + 1} invalid JSON: ${err.message}`);
|
|
70
|
+
}
|
|
71
|
+
if (obj.schema !== undefined) {
|
|
72
|
+
if (obj.schema !== 'tutien-generic-v1') {
|
|
73
|
+
throw new Error(`${file}:${i + 1} unsupported schema "${obj.schema}"; supported: tutien-generic-v1`);
|
|
74
|
+
}
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
if (!KNOWN_TYPES.has(obj.type)) bad(file, i + 1, `unknown event type "${obj.type}"`);
|
|
78
|
+
validateEvent(obj, file, i + 1);
|
|
79
|
+
events.push(obj);
|
|
80
|
+
}
|
|
81
|
+
return events;
|
|
82
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import process from 'node:process';
|
|
3
|
+
import { spawnSync } from 'node:child_process';
|
|
4
|
+
|
|
5
|
+
// Read-only Git metadata adapter, hard-bounded to the current repository:
|
|
6
|
+
// the requested root must resolve (through symlinks) to the same toplevel as
|
|
7
|
+
// the process working directory, or the adapter refuses. A revert commit is
|
|
8
|
+
// an issue *candidate* (confidence-scored downstream), never automatically a
|
|
9
|
+
// failure.
|
|
10
|
+
|
|
11
|
+
function repoToplevel(dir) {
|
|
12
|
+
const res = spawnSync('git', ['-C', dir, 'rev-parse', '--show-toplevel'], { encoding: 'utf8' });
|
|
13
|
+
if (res.status !== 0) {
|
|
14
|
+
throw new Error(`not a git repository: ${dir}`);
|
|
15
|
+
}
|
|
16
|
+
return fs.realpathSync(res.stdout.trim());
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function readGitEvents(repoRoot, limit = 500) {
|
|
20
|
+
const requested = repoToplevel(repoRoot);
|
|
21
|
+
const current = repoToplevel(process.cwd());
|
|
22
|
+
if (requested !== current) {
|
|
23
|
+
throw new Error(
|
|
24
|
+
`gitRoot ${repoRoot} resolves to ${requested}, outside the current repository ${current}; tutien only reads the current repo's history`
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
const res = spawnSync('git', ['log', '-n', String(limit), '--pretty=format:%H%x1f%aI%x1f%s%x1e'], {
|
|
28
|
+
cwd: requested,
|
|
29
|
+
encoding: 'utf8'
|
|
30
|
+
});
|
|
31
|
+
if (res.status !== 0) {
|
|
32
|
+
throw new Error(`git log failed in ${requested}: ${res.stderr?.trim() || res.status}`);
|
|
33
|
+
}
|
|
34
|
+
return res.stdout
|
|
35
|
+
.split('\x1e')
|
|
36
|
+
.map((s) => s.trim())
|
|
37
|
+
.filter(Boolean)
|
|
38
|
+
.map((rec) => {
|
|
39
|
+
const [hash, ts, subject] = rec.split('\x1f');
|
|
40
|
+
return {
|
|
41
|
+
type: 'commit',
|
|
42
|
+
session: 'git',
|
|
43
|
+
ts,
|
|
44
|
+
text: subject,
|
|
45
|
+
commit: { hash, isRevert: /^Revert\b/.test(subject) }
|
|
46
|
+
};
|
|
47
|
+
});
|
|
48
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// Plain-text transcript adapter. Roles come only from explicit line markers;
|
|
2
|
+
// token usage is always "unknown" here, never estimated silently.
|
|
3
|
+
|
|
4
|
+
const ROLE_RE = /^(user|human|assistant|ai|người dùng|trợ lý)\s*:\s*(.*)$/i;
|
|
5
|
+
const ROLE_MAP = {
|
|
6
|
+
user: 'user',
|
|
7
|
+
human: 'user',
|
|
8
|
+
'người dùng': 'user',
|
|
9
|
+
assistant: 'assistant',
|
|
10
|
+
ai: 'assistant',
|
|
11
|
+
'trợ lý': 'assistant'
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export function parseTranscript(content) {
|
|
15
|
+
const events = [];
|
|
16
|
+
const warnings = [];
|
|
17
|
+
let current = null;
|
|
18
|
+
for (const line of content.split(/\r?\n/)) {
|
|
19
|
+
const m = line.match(ROLE_RE);
|
|
20
|
+
if (m) {
|
|
21
|
+
if (current) events.push(current);
|
|
22
|
+
current = {
|
|
23
|
+
type: 'message',
|
|
24
|
+
source: 'transcript',
|
|
25
|
+
session: 'transcript',
|
|
26
|
+
role: ROLE_MAP[m[1].toLowerCase()],
|
|
27
|
+
text: m[2],
|
|
28
|
+
usage: { accuracy: 'unknown' }
|
|
29
|
+
};
|
|
30
|
+
} else if (current) {
|
|
31
|
+
current.text += `\n${line}`;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
if (current) events.push(current);
|
|
35
|
+
if (events.length === 0) warnings.push('no role markers found; transcript ignored');
|
|
36
|
+
return { events, warnings };
|
|
37
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import process from 'node:process';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
import { parseJsonl } from './adapters/generic-jsonl.mjs';
|
|
7
|
+
import { parseTranscript } from './adapters/plain-transcript.mjs';
|
|
8
|
+
import { readGitEvents } from './adapters/git.mjs';
|
|
9
|
+
import { normalizeEvents } from './normalize-events.mjs';
|
|
10
|
+
import {
|
|
11
|
+
groupTasks,
|
|
12
|
+
repetitionMetrics,
|
|
13
|
+
conflictCandidates,
|
|
14
|
+
issueMetrics,
|
|
15
|
+
tokenMetrics,
|
|
16
|
+
coverageMetrics
|
|
17
|
+
} from './metrics.mjs';
|
|
18
|
+
|
|
19
|
+
// Phase 1 analyzer: deterministic, read-only, machine-readable JSON only.
|
|
20
|
+
// The output contains digests and event IDs, never prompt text.
|
|
21
|
+
export function analyze({ jsonlFiles = [], transcriptFiles = [], gitRoot = null, gapMinutes = 60 } = {}) {
|
|
22
|
+
const notes = [];
|
|
23
|
+
let raw = [];
|
|
24
|
+
// The source tag is forced per adapter batch here; export content cannot
|
|
25
|
+
// spoof it (the JSONL validator additionally rejects reserved fields).
|
|
26
|
+
for (const file of jsonlFiles) {
|
|
27
|
+
raw = raw.concat(
|
|
28
|
+
parseJsonl(fs.readFileSync(file, 'utf8'), path.basename(file)).map((e) => ({ ...e, __source: 'generic-jsonl' }))
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
for (const file of transcriptFiles) {
|
|
32
|
+
const { events, warnings } = parseTranscript(fs.readFileSync(file, 'utf8'));
|
|
33
|
+
raw = raw.concat(events.map((e) => ({ ...e, __source: 'transcript' })));
|
|
34
|
+
notes.push(...warnings.map((w) => `${path.basename(file)}: ${w}`));
|
|
35
|
+
}
|
|
36
|
+
if (gitRoot) raw = raw.concat(readGitEvents(gitRoot).map((e) => ({ ...e, __source: 'git' })));
|
|
37
|
+
|
|
38
|
+
const events = normalizeEvents(raw);
|
|
39
|
+
const tasks = groupTasks(events, { gapMinutes });
|
|
40
|
+
const taskKeyByEvent = new Map();
|
|
41
|
+
for (const t of tasks) for (const id of t.eventIds) taskKeyByEvent.set(id, t.taskId);
|
|
42
|
+
const tokens = tokenMetrics(events);
|
|
43
|
+
return {
|
|
44
|
+
schemaVersion: 1,
|
|
45
|
+
eventsAnalyzed: events.length,
|
|
46
|
+
coverage: coverageMetrics(events, tasks, tokens),
|
|
47
|
+
tokens,
|
|
48
|
+
tasks: tasks.map((t) => ({
|
|
49
|
+
taskId: t.taskId,
|
|
50
|
+
sessionId: t.sessionId,
|
|
51
|
+
grouping: t.grouping,
|
|
52
|
+
confidence: t.confidence,
|
|
53
|
+
eventCount: t.eventIds.length
|
|
54
|
+
})),
|
|
55
|
+
commitEventIds: events.filter((e) => e.eventType === 'commit').map((e) => e.eventId),
|
|
56
|
+
repetition: repetitionMetrics(events, tasks),
|
|
57
|
+
conflicts: conflictCandidates(events, tasks),
|
|
58
|
+
issues: issueMetrics(events, taskKeyByEvent),
|
|
59
|
+
notes
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const isMain = process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url);
|
|
64
|
+
if (isMain) {
|
|
65
|
+
const args = process.argv.slice(2);
|
|
66
|
+
const spec = { jsonlFiles: [], transcriptFiles: [], gitRoot: null, gapMinutes: 60 };
|
|
67
|
+
let out = null;
|
|
68
|
+
for (let i = 0; i < args.length; i++) {
|
|
69
|
+
const a = args[i];
|
|
70
|
+
if (a === '--jsonl') spec.jsonlFiles.push(args[++i]);
|
|
71
|
+
else if (a === '--transcript') spec.transcriptFiles.push(args[++i]);
|
|
72
|
+
else if (a === '--git') spec.gitRoot = args[++i];
|
|
73
|
+
else if (a === '--gap-minutes') spec.gapMinutes = Number(args[++i]);
|
|
74
|
+
else if (a === '--out') out = args[++i];
|
|
75
|
+
else {
|
|
76
|
+
console.error(`Unknown argument: ${a}`);
|
|
77
|
+
console.error('Usage: analyze-history.mjs [--jsonl f]... [--transcript f]... [--git dir] [--gap-minutes n] [--out f]');
|
|
78
|
+
process.exit(2);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
const json = JSON.stringify(analyze(spec), null, 2);
|
|
82
|
+
if (out) fs.writeFileSync(out, `${json}\n`);
|
|
83
|
+
else console.log(json);
|
|
84
|
+
}
|