@litfamily/litopencode 1.0.23 → 1.0.25

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/CHANGELOG.md CHANGED
@@ -5,6 +5,14 @@ are tracked separately from this product history.
5
5
 
6
6
  ## Unreleased
7
7
 
8
+ ## 1.0.25 - 2026-10-15
9
+
10
+ - The write guard now refuses sentences that say what a document is not for, such as `동일 조건의 독립 성능 비교로 쓰지 않음.` or an English line saying a result is not a benchmark. When the model writes one with the write or edit tool, the call is refused and the model rewrites it. Three new rules cover Korean pledges, a Korean pledge repeated through a document, and the English forms, and the status-negation warning now also reads `쓰지 않음`, the polite endings and bold-wrapped endings. Legal and financial notices, safety warnings, operating rules, method lines and real negative findings stay untouched, and the rules still finish in milliseconds on very long runs of spaces. The lit-docx and lit-pptx skills now ask for a real limit once, as a fact beside the number it affects, and keep anything about what the document is not for in the reply.
11
+
12
+ ## 1.0.24 - 2026-10-14
13
+
14
+ - The LIT card, the chip beside the prompt, the sidebar panel and the home line now wear the LIT colours in every OpenCode theme: the `🔥 LIT` pill runs from pink to flame, the workflow map ramps from flame through pink to the LIT accent, the running stage and the current skills are accent pills, and warnings and the finished state use LIT amber and green. Text, panels and borders still follow your theme, and on a light theme the LIT colours deepen so they stay readable. The README pictures of the card show the new look.
15
+
8
16
  ## 1.0.23 - 2026-10-13
9
17
 
10
18
  - The README demo on GitHub is a new film of one real OpenCode turn with the workflow map. A numbered label names each part as it appears: the `lit` word, the LIT IGNITED toast, the card's map skipping interview and plan to light WORK, the new test, the running tool and its time beside the map (`bash · 0:00` while `npm test` runs), the passing test run and the card settling to IDLE when the turn ends. It still plays without music.
package/README-Ko-KR.md CHANGED
@@ -1,13 +1,13 @@
1
- <p align="center"><picture><source media="(prefers-reduced-motion: reduce)" srcset="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.23/docs/assets/cover-motion-still.webp" /><img src="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.23/docs/assets/cover-motion.webp" width="100%" alt="LitFamily 모션 커버: 다섯 로봇 패널이 차례로 켜지고, LitOpenCode 로봇의 눈과 테두리가 빛난 뒤 LITFAMILY와 KEEP THE WORK LIT. 문구가 밝아지는 영상" /></picture></p>
1
+ <p align="center"><picture><source media="(prefers-reduced-motion: reduce)" srcset="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.25/docs/assets/cover-motion-still.webp" /><img src="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.25/docs/assets/cover-motion.webp" width="100%" alt="LitFamily 모션 커버: 다섯 로봇 패널이 차례로 켜지고, LitOpenCode 로봇의 눈과 테두리가 빛난 뒤 LITFAMILY와 KEEP THE WORK LIT. 문구가 밝아지는 영상" /></picture></p>
2
2
 
3
3
  # LitOpenCode
4
4
 
5
5
  **LitOpenCode는 OpenCode용 워크플로 플러그인입니다. 프롬프트에 `lit`만 붙이면 계획하고, 만들고, 확인하고, 기록까지 남겨서 다음 세션이 어디서 멈췄는지 바로 압니다.**
6
6
 
7
7
  <p align="center">
8
- <a href="https://www.npmjs.com/package/@litfamily/litopencode"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.23/docs/assets/readme/badge-version.svg" alt="1.0.23" /></a>
8
+ <a href="https://www.npmjs.com/package/@litfamily/litopencode"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.25/docs/assets/readme/badge-version.svg" alt="1.0.25" /></a>
9
9
  <a href="https://www.npmjs.com/package/@litfamily/litopencode"><img src="https://img.shields.io/npm/dm/@litfamily/litopencode?label=downloads&color=D7F75B" alt="월간 npm 다운로드 수" /></a>
10
- <a href="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.23/LICENSE"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.23/docs/assets/readme/badge-license.svg" alt="MIT 라이선스" /></a>
10
+ <a href="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.25/LICENSE"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.25/docs/assets/readme/badge-license.svg" alt="MIT 라이선스" /></a>
11
11
  <a href="https://github.com/wjgoarxiv/litopencode"><img src="https://img.shields.io/github/stars/wjgoarxiv/litopencode?style=social" alt="GitHub 스타" /></a>
12
12
  </p>
13
13
 
@@ -57,9 +57,9 @@ npm uninstall -g @litfamily/litopencode
57
57
  ## 더 보기
58
58
 
59
59
  - [GitHub의 사용 안내](https://github.com/wjgoarxiv/litopencode/blob/main/docs/guide-Ko-KR.md): 설치 옵션, 자동 인수인계, 문제 해결, 제거
60
- - [워크플로 상세 안내](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.23/docs/reference-Ko-KR.md) · [English reference](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.23/docs/reference.md)
61
- - [마이그레이션 안내](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.23/docs/migration.md) · [Changelog](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.23/CHANGELOG.md) · [개인정보와 네트워크 동작](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.23/docs/privacy.md)
62
- - [LitFamily 허브](https://github.com/wjgoarxiv/litfamily) · [MIT 라이선스](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.23/LICENSE) · [ASCII 글꼴 라이선스](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.23/docs/assets/readme/JetBrainsMono-OFL.txt)
60
+ - [워크플로 상세 안내](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.25/docs/reference-Ko-KR.md) · [English reference](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.25/docs/reference.md)
61
+ - [마이그레이션 안내](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.25/docs/migration.md) · [Changelog](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.25/CHANGELOG.md) · [개인정보와 네트워크 동작](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.25/docs/privacy.md)
62
+ - [LitFamily 허브](https://github.com/wjgoarxiv/litfamily) · [MIT 라이선스](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.25/LICENSE) · [ASCII 글꼴 라이선스](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.25/docs/assets/readme/JetBrainsMono-OFL.txt)
63
63
 
64
64
  <details>
65
65
  <summary>ASCII 로고 복사</summary>
package/README.md CHANGED
@@ -1,13 +1,13 @@
1
- <p align="center"><picture><source media="(prefers-reduced-motion: reduce)" srcset="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.23/docs/assets/cover-motion-still.webp" /><img src="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.23/docs/assets/cover-motion.webp" width="100%" alt="LitFamily motion cover: five armored robots power on one by one, the LitOpenCode robot wakes with glowing eyes and a lit frame, then LITFAMILY and KEEP THE WORK LIT. light up." /></picture></p>
1
+ <p align="center"><picture><source media="(prefers-reduced-motion: reduce)" srcset="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.25/docs/assets/cover-motion-still.webp" /><img src="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.25/docs/assets/cover-motion.webp" width="100%" alt="LitFamily motion cover: five armored robots power on one by one, the LitOpenCode robot wakes with glowing eyes and a lit frame, then LITFAMILY and KEEP THE WORK LIT. light up." /></picture></p>
2
2
 
3
3
  # LitOpenCode
4
4
 
5
5
  **LitOpenCode is a workflow plugin for OpenCode. Add `lit` to a prompt and the work gets planned, built, checked and written down, so the next session knows where you stopped.**
6
6
 
7
7
  <p align="center">
8
- <a href="https://www.npmjs.com/package/@litfamily/litopencode"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.23/docs/assets/readme/badge-version.svg" alt="1.0.23" /></a>
8
+ <a href="https://www.npmjs.com/package/@litfamily/litopencode"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.25/docs/assets/readme/badge-version.svg" alt="1.0.25" /></a>
9
9
  <a href="https://www.npmjs.com/package/@litfamily/litopencode"><img src="https://img.shields.io/npm/dm/@litfamily/litopencode?label=downloads&color=D7F75B" alt="npm downloads per month" /></a>
10
- <a href="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.23/LICENSE"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.23/docs/assets/readme/badge-license.svg" alt="MIT license" /></a>
10
+ <a href="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.25/LICENSE"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.25/docs/assets/readme/badge-license.svg" alt="MIT license" /></a>
11
11
  <a href="https://github.com/wjgoarxiv/litopencode"><img src="https://img.shields.io/github/stars/wjgoarxiv/litopencode?style=social" alt="GitHub stars" /></a>
12
12
  </p>
13
13
 
@@ -57,9 +57,9 @@ npm uninstall -g @litfamily/litopencode
57
57
  ## More
58
58
 
59
59
  - [Guide on GitHub](https://github.com/wjgoarxiv/litopencode/blob/main/docs/guide.md): install options, automatic handoff, troubleshooting and removal.
60
- - [Workflow reference](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.23/docs/reference.md)
61
- - [Migration notes](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.23/docs/migration.md) · [Changelog](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.23/CHANGELOG.md) · [Privacy and network behavior](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.23/docs/privacy.md)
62
- - [LitFamily hub](https://github.com/wjgoarxiv/litfamily) · [MIT license](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.23/LICENSE) · [ASCII font license](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.23/docs/assets/readme/JetBrainsMono-OFL.txt)
60
+ - [Workflow reference](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.25/docs/reference.md)
61
+ - [Migration notes](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.25/docs/migration.md) · [Changelog](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.25/CHANGELOG.md) · [Privacy and network behavior](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.25/docs/privacy.md)
62
+ - [LitFamily hub](https://github.com/wjgoarxiv/litfamily) · [MIT license](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.25/LICENSE) · [ASCII font license](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.25/docs/assets/readme/JetBrainsMono-OFL.txt)
63
63
 
64
64
  <details>
65
65
  <summary>Copy ASCII logo</summary>
@@ -23,7 +23,7 @@ export type NotifyOptions = {
23
23
  */
24
24
  export declare function readNotifyOptions(options: unknown): NotifyOptions;
25
25
  /**
26
- * The skill chips row, exactly `width` cells: bright accent pills for the current request first, then
26
+ * The skill chips row, exactly `width` cells: bright LIT accent pills for the current request first, then
27
27
  * dim recent names, and `+N` for the ones that do not fit. The label goes before any chip does.
28
28
  */
29
29
  export declare function skillRow(chips: readonly SkillChip[], shimmer: ChipShimmer | undefined, palette: MissionPalette, width: number): Line;
@@ -130,7 +130,7 @@ function phaseLabel(view, frame, palette, rest) {
130
130
  return { text: `● ${stepName(view.phase, view).toUpperCase()}`, fg: palette.info, bold: true };
131
131
  }
132
132
  function topLine(view, frame, palette, width, border, rest) {
133
- const brand = [{ text: "🔥 ", fg: palette.text }, { text: "LIT", fg: shine(palette.primary, palette, 0.5, shimmerAt(frame), 0.6), bold: true }];
133
+ const brand = [{ text: "🔥 ", fg: palette.text }, { text: "LIT", fg: shine(palette.secondary, palette, 0.5, shimmerAt(frame), 0.6), bold: true }];
134
134
  const name = view.active ? view.discipline : "ready";
135
135
  const right = [phaseLabel(view, frame, palette, rest)];
136
136
  const withName = (room) => [...brand, { text: " · ", fg: palette.muted }, { text: truncateCells(name, room), fg: view.active ? palette.text : palette.muted }];
@@ -237,12 +237,12 @@ function chipSegments(chip, shimmer, palette) {
237
237
  return [{ text, fg: palette.muted, ...(palette.element === undefined ? {} : { bg: palette.element }) }];
238
238
  const sweep = shimmer?.names.includes(chip.name) === true ? shimmer.sweep : -1;
239
239
  if (sweep < 0)
240
- return [{ text, fg: palette.onFill, bg: palette.accent, bold: true }];
240
+ return [{ text, fg: palette.onFill, bg: palette.fill, bold: true }];
241
241
  const chars = [...text];
242
- return chars.map((char, index) => ({ text: char, fg: palette.onFill, bg: shine(palette.accent, palette, index / (chars.length - 1), sweep, 0.3), bold: true }));
242
+ return chars.map((char, index) => ({ text: char, fg: palette.onFill, bg: shine(palette.fill, palette, index / (chars.length - 1), sweep, 0.3), bold: true }));
243
243
  }
244
244
  /**
245
- * The skill chips row, exactly `width` cells: bright accent pills for the current request first, then
245
+ * The skill chips row, exactly `width` cells: bright LIT accent pills for the current request first, then
246
246
  * dim recent names, and `+N` for the ones that do not fit. The label goes before any chip does.
247
247
  */
248
248
  export function skillRow(chips, shimmer, palette, width) {
@@ -25,6 +25,9 @@ export type MissionPalette = {
25
25
  readonly element?: string;
26
26
  /** What fades and tints mix toward: the panel, else the background, else a neutral for the mode. */
27
27
  readonly mixBase: string;
28
+ /** The running stage's pill and the current skill chip, and that pill on a warning: bright in every mode. */
29
+ readonly fill: string;
30
+ readonly fillWarn: string;
28
31
  /** Text drawn on a filled pill. */
29
32
  readonly onFill: string;
30
33
  };
@@ -34,8 +37,9 @@ export declare const panelInnerWidth: number;
34
37
  /** Theme tokens arrive as RGBA objects; plain hex strings pass through. Alpha is ignored here. */
35
38
  export declare function colorToHex(value: unknown, fallback: string): string;
36
39
  /**
37
- * Builds the palette from `api.theme.current`. A surface token with alpha 0 (the system and
38
- * lucent-orng themes) means "draw no background", never black.
40
+ * Builds the palette from `api.theme.current`: the LIT signature colours for the brand, the map ramp,
41
+ * the pills, the bar and the state colours, over the theme's text, muted text, fills and borders.
42
+ * A surface token with alpha 0 (the system and lucent-orng themes) means "draw no background", never black.
39
43
  */
40
44
  export declare function paletteFromTheme(theme: Record<string, unknown> | undefined, mode?: "dark" | "light"): MissionPalette;
41
45
  /**
@@ -62,7 +66,7 @@ export declare function shimmerAt(frame: MotionFrame): number;
62
66
  * primary-to-accent gradient. The empty track is a thin muted rule, never a dither.
63
67
  */
64
68
  export declare function progressBar(ratio: number, cells: number, palette: MissionPalette, sweep?: number): Segment[];
65
- /** The stage colour ramp: the theme's primary through secondary to accent, so every theme keeps its own feel. */
69
+ /** The stage colour ramp: LIT flame through pink to the LIT accent. */
66
70
  export declare function stageColor(index: number, count: number, palette: MissionPalette): string;
67
71
  /** A light pass over the running stage: once when the map moves forward, and during the completion moment. */
68
72
  export declare function stageFlash(frame: MotionFrame): number;
@@ -3,6 +3,16 @@
3
3
  // renders as spans. Every frame of motion is drawn from a MotionFrame, so all of it is testable here.
4
4
  import { ease, mixHex, rowEntryStart, seg, slideIn, motionTiming } from "./mission-motion.js";
5
5
  import { currentStepIndex, missionRowKeys, missionStatus, stepName } from "./mission-model.js";
6
+ /**
7
+ * The LIT signature colours. Dark themes use them as they are; light themes deepen the colours drawn
8
+ * as text within the same hue so words stay readable, while pill fills keep the bright signature with
9
+ * dark ink in both modes.
10
+ */
11
+ const litColors = {
12
+ dark: { flame: "#ff6337", pink: "#ff2d95", accent: "#5fd7ff", green: "#6bdb8f", amber: "#ffaf3c", red: "#ff5f5f" },
13
+ light: { flame: "#e2521f", pink: "#d81b78", accent: "#0a87b8", green: "#1f9d55", amber: "#b86e00", red: "#d93a3a" },
14
+ ink: "#0b0f14"
15
+ };
6
16
  /** Panel box width in cells, and the content width inside its border and one cell of padding per side. */
7
17
  export const panelWidth = 36;
8
18
  export const panelInnerWidth = panelWidth - 4;
@@ -34,8 +44,9 @@ export function colorToHex(value, fallback) {
34
44
  return readColor(value)?.hex ?? fallback;
35
45
  }
36
46
  /**
37
- * Builds the palette from `api.theme.current`. A surface token with alpha 0 (the system and
38
- * lucent-orng themes) means "draw no background", never black.
47
+ * Builds the palette from `api.theme.current`: the LIT signature colours for the brand, the map ramp,
48
+ * the pills, the bar and the state colours, over the theme's text, muted text, fills and borders.
49
+ * A surface token with alpha 0 (the system and lucent-orng themes) means "draw no background", never black.
39
50
  */
40
51
  export function paletteFromTheme(theme, mode = "dark") {
41
52
  const pick = (key, fallback) => colorToHex(theme?.[key], fallback);
@@ -47,14 +58,15 @@ export function paletteFromTheme(theme, mode = "dark") {
47
58
  const panel = surface("backgroundPanel");
48
59
  const element = surface("backgroundElement");
49
60
  const neutral = mode === "light" ? "#f2f2f2" : "#1c1c1c";
61
+ const lit = litColors[mode];
50
62
  return {
51
- primary: pick("primary", "#ff6337"),
52
- secondary: pick("secondary", "#ff2d95"),
53
- accent: pick("accent", "#b48cff"),
54
- info: pick("info", "#0aa6c2"),
55
- warning: pick("warning", "#ffaf3c"),
56
- success: pick("success", "#6bdb8f"),
57
- error: pick("error", "#ff5f5f"),
63
+ primary: lit.flame,
64
+ secondary: lit.pink,
65
+ accent: lit.accent,
66
+ info: lit.accent,
67
+ warning: lit.amber,
68
+ success: lit.green,
69
+ error: lit.red,
58
70
  text: pick("text", "#e6edf3"),
59
71
  muted: pick("textMuted", "#7d8590"),
60
72
  borderActive: pick("borderActive", "#484f58"),
@@ -63,7 +75,9 @@ export function paletteFromTheme(theme, mode = "dark") {
63
75
  ...(panel === undefined ? {} : { panel }),
64
76
  ...(element === undefined ? {} : { element }),
65
77
  mixBase: panel ?? background ?? neutral,
66
- onFill: background ?? panel ?? (mode === "light" ? "#ffffff" : "#0b0b0b")
78
+ fill: litColors.dark.accent,
79
+ fillWarn: litColors.dark.amber,
80
+ onFill: litColors.ink
67
81
  };
68
82
  }
69
83
  /**
@@ -183,7 +197,7 @@ export function progressBar(ratio, cells, palette, sweep = -1) {
183
197
  segments.push({ text: "─".repeat(cells - used), fg: palette.borderSubtle });
184
198
  return segments;
185
199
  }
186
- /** The stage colour ramp: the theme's primary through secondary to accent, so every theme keeps its own feel. */
200
+ /** The stage colour ramp: LIT flame through pink to the LIT accent. */
187
201
  export function stageColor(index, count, palette) {
188
202
  return gradientAt([palette.primary, palette.secondary, palette.accent], count <= 1 ? 0 : index / (count - 1));
189
203
  }
@@ -218,7 +232,7 @@ function pillStyle(state, palette, warn) {
218
232
  if (state === "todo" || state === "skip")
219
233
  return { fg: palette.muted, ...(palette.panel === undefined ? {} : { bg: palette.panel }), bold: false };
220
234
  const level = state === "warn" ? Math.max(warn, 0.5) : warn;
221
- return { fg: palette.onFill, bg: mixHex(palette.info, palette.warning, level), bold: true };
235
+ return { fg: palette.onFill, bg: mixHex(palette.fill, palette.fillWarn, level), bold: true };
222
236
  }
223
237
  /** A light pass over the running stage: once when the map moves forward, and during the completion moment. */
224
238
  export function stageFlash(frame) {
@@ -265,7 +279,7 @@ export function crumb(view, index, frame, palette, bar, flash = -1) {
265
279
  const chars = [...label];
266
280
  const pill = flash < 0
267
281
  ? [{ text: label, fg: style.fg, ...(style.bg === undefined ? {} : { bg: style.bg }), bold: style.bold }]
268
- : chars.map((char, position) => ({ text: char, fg: style.fg, bg: shine(style.bg ?? palette.info, palette, position / (chars.length - 1), flash, 0.3), bold: true }));
282
+ : chars.map((char, position) => ({ text: char, fg: style.fg, bg: shine(style.bg ?? palette.fill, palette, position / (chars.length - 1), flash, 0.3), bold: true }));
269
283
  if (!counted)
270
284
  return pill;
271
285
  return [...pill, ...(bar > 0 ? [{ text: " ", fg: palette.muted }, ...microBar(frame.barRatio, palette, bar)] : []), { text: ` ${countText(view)}`, fg: palette.text, bold: true }];
@@ -292,9 +306,13 @@ export function liveSegments(view, frame, palette, room) {
292
306
  { text: time, fg: palette.muted }
293
307
  ];
294
308
  }
309
+ /** The `🔥 LIT` pill: LIT pink to flame across its cells with dark ink, bright on light themes too. */
295
310
  function brandPill(palette, sweep) {
296
311
  const chars = [" ", "🔥", " ", "L", "I", "T", " "];
297
- return chars.map((char, index) => ({ text: char, fg: palette.onFill, bg: shine(palette.primary, palette, index / (chars.length - 1), sweep, 0.3), bold: true }));
312
+ return chars.map((char, index) => {
313
+ const position = index / (chars.length - 1);
314
+ return { text: char, fg: palette.onFill, bg: shine(mixHex(litColors.dark.pink, litColors.dark.flame, position), palette, position, sweep, 0.3), bold: true };
315
+ });
298
316
  }
299
317
  /**
300
318
  * The chip's groups for a tier. The full tier is the brand and the breadcrumb around the running stage
@@ -1 +1 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" width="146" height="20" role="img" aria-label="release: 1.0.23"><title>release: 1.0.23</title><g shape-rendering="crispEdges"><rect width="49" height="20" fill="#080d14"/><rect x="49" width="97" height="20" fill="#ff6337"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text x="255" y="140" textLength="390" transform="scale(.1)">release</text><text x="965" y="140" textLength="870" transform="scale(.1)" fill="#080D14">1.0.23</text></g></svg>
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="146" height="20" role="img" aria-label="release: 1.0.25"><title>release: 1.0.25</title><g shape-rendering="crispEdges"><rect width="49" height="20" fill="#080d14"/><rect x="49" width="97" height="20" fill="#ff6337"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text x="255" y="140" textLength="390" transform="scale(.1)">release</text><text x="965" y="140" textLength="870" transform="scale(.1)" fill="#080D14">1.0.25</text></g></svg>
@@ -269,8 +269,8 @@ OpenCode 1.18은 `tui.json`이나 `tui.jsonc`의 `plugin` 배열에 적힌 TUI
269
269
  단계를 건너뛴 것으로 보여 주고, 했다고 표시하지 않습니다. `review-work` 없이 할 일만 모두 끝내도
270
270
  review는 `–`로 남습니다. 리뷰 명령, 리뷰 막힘, ledger CLI로 끝낸 목표(완료 관문이 리뷰를 포함함)가 있어야
271
271
  완료로 바뀝니다. 에이전트가 직접 쓴 `goals.json`에서 끝났다고 표시한 목표는 관문을 거치지 않았으므로
272
- review를 `–`로 둡니다. 지나온 단계는 테마의 primary에서
273
- secondary를 거쳐 accent로 가는 색을 차례로 받고, 앞으로 할 단계는 흐리게 그립니다.
272
+ review를 `–`로 둡니다. 지나온 단계는 LIT 불꽃색에서
273
+ 분홍색을 거쳐 LIT accent로 가는 색을 차례로 받고, 앞으로 할 단계는 흐리게 그립니다.
274
274
  - 입력창 아래 agent/model 줄 오른쪽의 지도 칩: `🔥 LIT` 알약 뒤에 지금 단계 앞뒤를 이어 쓴
275
275
  `plan ✓ › WORK ▰▰▰▱▱▱▱ 3/7 › review`가 붙습니다. 지금 단계는 채운 알약과 일곱 칸 막대로 보입니다.
276
276
  자리가 넉넉하면 지금 하는 일(`⠹ bash · 0:48`)이 뒤따릅니다. 자리가 좁으면 지금 하는 일을 먼저 빼고,
@@ -278,8 +278,8 @@ OpenCode 1.18은 `tui.json`이나 `tui.jsonc`의 `plugin` 배열에 적힌 TUI
278
278
  개수만 남깁니다. 자리는 OpenCode에 저장된 사이드바 설정과 세션 종류(하위 세션에는 사이드바가
279
279
  없음)로 계산합니다. 설정을 아직 읽을 수 없으면 사이드바가 열려 있다고 보고 좁게 잡습니다.
280
280
  - 세션 사이드바 맨 위의 미션 패널: 36칸짜리 둥근 상자이고, 위 테두리에 `🔥 LIT ▸ <단계>`, 아래
281
- 테두리에 `n/m criteria`가 붙습니다. 테두리 색이 상태를 보여 줍니다. 일하는 중에는 테마 accent,
282
- 경고가 있으면 warning, 끝나면 success 색입니다. 안에는 두 줄짜리 `LIT`과 그 옆의 작업 이름·상태,
281
+ 테두리에 `n/m criteria`가 붙습니다. 테두리 색이 상태를 보여 줍니다. 일하는 중에는 LIT accent,
282
+ 경고가 있으면 LIT 호박색, 끝나면 LIT 초록색입니다. 안에는 두 줄짜리 `LIT`과 그 옆의 작업 이름·상태,
283
283
  굵은 목표 제목, 세로 단계 지도가 들어갑니다. 지도에서 지난 단계는 `◆`, 지금 단계는 굵은 `◉`,
284
284
  경고가 걸린 단계는 `⚑`, 남은 단계는 `◇`입니다. 지금 단계까지는 단계 색의 `┃`로, 그 뒤로는 점선
285
285
  `┆`로 잇고, plan 옆에는 저장된 계획의 할 일 수, work 옆에는 `iter k`를 적습니다. 그 아래로 1/8칸
@@ -298,7 +298,7 @@ OpenCode 1.18은 `tui.json`이나 `tui.jsonc`의 `plugin` 배열에 적힌 TUI
298
298
  일과 `▸ 다음 일` 자리에 `⚑ <내용>`이 들어갑니다. 쉬는 세션에서는 흐린 지도 옆에 프로젝트의
299
299
  마지막 목표를, 목표가 없으면 `add lit to a prompt to start`를 보여 줍니다.
300
300
  - 둘째 줄은 스킬 칩입니다. 세션에서 실행된 LitOpenCode 스킬이 여러 개 함께 보입니다. 지금 요청에서
301
- 실행된 스킬은 요청이 도는 동안 accent 색 알약으로 밝게 보이고, 새로 들어온 칩에는 빛이 짧게 한 번
301
+ 실행된 스킬은 요청이 도는 동안 LIT accent 색 알약으로 밝게 보이고, 새로 들어온 칩에는 빛이 짧게 한 번
302
302
  지나갑니다. 턴이 끝나면 흐려져 최근 목록으로 남고(최근 것이 앞), 다시 실행되면 다시 밝아집니다.
303
303
  자리가 모자라면 남은 칩을 `+N`으로 접습니다.
304
304
  - 아래 테두리에는 context 사용량(75%부터 주황), 첫 lit 턴부터 지난 시간(`⟳ 7m`), git 브랜치가
@@ -310,8 +310,8 @@ OpenCode 1.18은 `tui.json`이나 `tui.jsonc`의 `plugin` 배열에 적힌 TUI
310
310
  넘기지는 않습니다. 완료 순간도 여기서 함께 보입니다.
311
311
  - 홈 화면 입력창 아래, OpenCode 팁 다음 줄: `🔥 lit ready · last: <목표> ✓ n/m`. 프로젝트 ledger에
312
312
  목표가 있을 때만 보입니다.
313
- - 경고는 색만 바꾸지 않고 늘 `⚑`와 글자를 함께 붙입니다. accent와 warning이 같은 색인 테마가 있기
314
- 때문입니다. 알파가 0인 테마 바탕(system, lucent-orng)은 검게 칠하지 않고 비워 둡니다.
313
+ - 경고는 색만 바꾸지 않고 늘 `⚑`와 글자를 함께 붙입니다. 색이 없어도 똑같이 읽히게 하려는
314
+ 것입니다. 알파가 0인 테마 바탕(system, lucent-orng)은 검게 칠하지 않고 비워 둡니다.
315
315
  - 관문 사건마다 토스트 하나: 계획 저장 또는 저장 실패, 목표 설정, 기준 통과, 리뷰 막힘과 해결, 완료.
316
316
  한 번에 여러 사건이 생기면 가장 급한 것 하나로 합칩니다.
317
317
  - 목표가 끝나거나, 할 일이 모두 끝나거나, 조사 실행이 보고서를 쓰면 1.2초 안에 끝나는 완료 순간을
@@ -361,8 +361,15 @@ ease-out-back으로 160ms씩, 60ms 간격으로 들어오고, 막대는 300ms에
361
361
  한 번 빛납니다. 계속 도는 움직임은 없습니다. 지금 하는 일은 미끄러져 들어오지 않고 제자리에서
362
362
  바뀌며, 도구가 도는 동안에만 걸린 시간이 1초에 한 번 바뀝니다. 도는 도구가 없으면 타이머도
363
363
  없습니다. lit 세션이 열려 있는 동안만 2초마다 목표 ledger, 범위 작업 기록, 조사 폴더를 다시 읽고,
364
- 쉬는 세션은 읽지 않습니다. 색은 지금 OpenCode
365
- 테마에서 가져옵니다. 움직임을 끄려면 `tui.json`에 `["@litfamily/litopencode@<version>", { "motion": false }]`
364
+ 쉬는 세션은 읽지 않습니다.
365
+
366
+ LIT 부분은 어떤 테마에서든 LIT 색을 씁니다. `🔥 LIT` 알약은 분홍(`#ff2d95`)에서 불꽃색(`#ff6337`)으로
367
+ 넘어가고, 단계 색은 불꽃색, 분홍, LIT accent(`#5fd7ff`) 순서로 이어집니다. 지금 단계와 지금 요청의
368
+ 스킬 칩은 accent 알약이고, 막대는 불꽃색에서 accent로 가며, 상태 색은 LIT 호박색(`#ffaf3c`),
369
+ 초록색(`#6bdb8f`), 빨간색(`#ff5f5f`)입니다. 알약 글자는 늘 어두운 색(`#0b0f14`)입니다. 글자, 흐린 글자, 패널과
370
+ 요소 바탕, 테두리, 투명한 바탕은 지금 OpenCode 테마에서 가져옵니다. 밝은 테마에서는 알약 바탕을
371
+ 밝게 그대로 두고, 글자로 쓰는 LIT 색만 같은 색조 안에서 진하게 바꿔(불꽃색 `#e2521f`, 분홍
372
+ `#d81b78`, accent `#0a87b8`, 초록 `#1f9d55`, 호박색 `#b86e00`, 빨강 `#d93a3a`) 흰 바탕에서도 읽히게 합니다. 움직임을 끄려면 `tui.json`에 `["@litfamily/litopencode@<version>", { "motion": false }]`
366
373
  처럼 적고, 플러그인 자체를 끄려면 OpenCode 플러그인 관리자나 `plugin_enabled`에서 id `litopencode`를 끕니다.
367
374
 
368
375
  띠 설정도 같은 옵션 객체에 적습니다.
package/docs/reference.md CHANGED
@@ -372,7 +372,7 @@ What it draws:
372
372
  at `–`. A review command, a review blocker or a goal completed through the ledger CLI (whose gate
373
373
  includes review) marks it done; a goal the agent marks complete in a hand-written `goals.json`
374
374
  passed no gate and leaves review at `–`. Stages behind the running one take their place on a colour
375
- ramp from the theme's primary through secondary to accent; stages ahead are muted.
375
+ ramp from LIT flame through pink to the LIT accent; stages ahead are muted.
376
376
  - A map chip on the right of the prompt's agent/model row: a `🔥 LIT` pill and the breadcrumb around
377
377
  the running stage, `plan ✓ › WORK ▰▰▰▱▱▱▱ 3/7 › review`, with the running stage as a filled pill and a
378
378
  seven-cell bar. When the row has room the live step follows (`⠹ bash · 0:48`). Narrower, it drops
@@ -382,7 +382,7 @@ What it draws:
382
382
  read yet, the chip assumes the sidebar is open and stays narrower.
383
383
  - A mission panel at the top of the session sidebar, in a 36-cell rounded box titled
384
384
  `🔥 LIT ▸ <STAGE>` with `n/m criteria` in the bottom border. The border carries the state: the
385
- theme accent while working, the warning colour on a warning, the success colour when complete.
385
+ LIT accent while working, LIT amber on a warning, LIT green when complete.
386
386
  Inside: a two-row `LIT` with the discipline and a status beside it, the goal title in bold, the
387
387
  vertical stage map (`◆` behind, `◉` running in bold, `⚑` flagged, `◇` ahead, joined by `┃`
388
388
  connectors in the ramp colour up to the running stage and dotted `┆` after it, with the saved
@@ -405,7 +405,7 @@ What it draws:
405
405
  warning is open, `⚑ <text>` takes the place of the live step and `▸ next`. An idle session shows
406
406
  the dim map with the project's last goal, or `add lit to a prompt to start`;
407
407
  - row two holds the skill chips: every LitOpenCode skill that fired in the session, several at
408
- once. Skills fired by the current request are bright accent pills while it runs, and a new one
408
+ once. Skills fired by the current request are bright LIT accent pills while it runs, and a new one
409
409
  gets a short light sweep as it arrives; when the turn ends they dim and stay as the recent list,
410
410
  newest first, and firing one again brightens it. Chips that do not fit collapse into `+N`;
411
411
  - the bottom border holds the context use (amber from 75%), the time since the first lit turn
@@ -417,8 +417,8 @@ What it draws:
417
417
  then the time, then the context use. It never wraps. The completion moment plays here too.
418
418
  - A line under the home prompt, after OpenCode's tips: `🔥 lit ready · last: <goal> ✓ n/m`. It
419
419
  appears only when the project ledger holds a goal.
420
- - Warnings always carry `⚑` and words, not colour alone, because some themes use one colour for
421
- both accent and warning. Theme surfaces with alpha 0 (the system and lucent-orng themes) are left
420
+ - Warnings always carry `⚑` and words, not colour alone, so they read the same without colour.
421
+ Theme surfaces with alpha 0 (the system and lucent-orng themes) are left
422
422
  unfilled rather than painted black.
423
423
  - One toast per refresh for a gate event: plan saved or not saved, goal set, criterion passed,
424
424
  review blocker opened or resolved, and completion. Several events at once collapse into the most
@@ -475,8 +475,16 @@ apart and the running pill flashes once; nothing loops. The live step changes in
475
475
  slide-in, and while a tool runs its elapsed time ticks once a second; with no tool running there is
476
476
  no timer at all. While a lit session is open the panel re-reads the goal ledger, the bounded work
477
477
  record and the research folder every two seconds; an idle session does not.
478
- Colours come from the active OpenCode theme. Set `["@litfamily/litopencode@<version>", { "motion": false }]`
479
- in `tui.json` to keep it static, or turn the plugin off by id `litopencode` in OpenCode's plugin
478
+
479
+ The LIT parts wear the LIT colours in every theme: the `🔥 LIT` pill runs from pink (`#ff2d95`) to
480
+ flame (`#ff6337`), the stage ramp runs flame, pink, then the LIT accent (`#5fd7ff`), the running stage
481
+ and the current skill chips are accent pills, the bar runs flame to accent, and the state colours are
482
+ LIT amber (`#ffaf3c`), green (`#6bdb8f`) and red (`#ff5f5f`). Pills always use dark ink (`#0b0f14`). Text, muted
483
+ text, the panel and element fills, the borders and transparent surfaces still come from the active
484
+ OpenCode theme. On a light theme the pill fills stay bright, and the LIT colours drawn as text
485
+ deepen within the same hue (flame `#e2521f`, pink `#d81b78`, accent `#0a87b8`, green `#1f9d55`, amber
486
+ `#b86e00`, red `#d93a3a`) so they stay readable on a white background. Set
487
+ `["@litfamily/litopencode@<version>", { "motion": false }]` in `tui.json` to turn the motion off, or turn the plugin off by id `litopencode` in OpenCode's plugin
480
488
  manager or with `plugin_enabled`.
481
489
 
482
490
  Band options go in the same options object:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@litfamily/litopencode",
3
- "version": "1.0.23",
3
+ "version": "1.0.25",
4
4
  "description": "Workflow plugin for OpenCode: add lit to a prompt and the work is planned, built, checked and written down so the next session can pick it up.",
5
5
  "keywords": [
6
6
  "opencode",
@@ -116,7 +116,7 @@ State material limitations, including a blocked export or visual check, in the u
116
116
 
117
117
  ## #contract.evidence
118
118
 
119
- Retain the direction card, conversion output, the gate JSON, and rendered PNG pages in the task's evidence location. Open the rendered pages; file existence alone cannot establish typography or pagination. Keep uncertainty and source attribution in the document itself where a reader needs them.
119
+ Retain the direction card, conversion output, the gate JSON, and rendered PNG pages in the task's evidence location. Open the rendered pages; file existence alone cannot establish typography or pagination. Write a real uncertainty once, as a fact about the data or method beside the number it affects, and keep source attribution beside the figure; what the document is not for goes in the reply.
120
120
 
121
121
  ## #contract.hard_stops
122
122
 
@@ -53,7 +53,7 @@ in a hanging indent wide enough for the widest number, so the heading texts of a
53
53
 
54
54
  - Do not use a `::: cover` directive unless you want a kicker line above the title; the control block
55
55
  comes from the frontmatter.
56
- - Open with scope and responsibilities: what the manual covers, what it does not, who does what.
56
+ - Open with scope and responsibilities: what the manual covers and who does what, stated as facts (which line, which equipment, which site); a list of what it is not for belongs in the reply.
57
57
  - Write each procedure as a Markdown numbered list (`1.`, `2.`, …), one action per step, in the
58
58
  imperative. Each new list restarts at 1.
59
59
  - Put conditions and values in tables (inspection items with their limits and the action when a limit is
@@ -50,7 +50,10 @@
50
50
  "en-chatbot-opener": "Certainly! Here is the overview.",
51
51
  "en-testament": "The project stands as a testament to careful planning.",
52
52
  "en-left-in-narration": "I've updated the file and saved it.",
53
- "en-chatbot-closer": "Let me know if you would like another version."
53
+ "en-chatbot-closer": "Let me know if you would like another version.",
54
+ "ko-negative-pledge": "동일 조건의 독립 성능 비교로 쓰지 않음.",
55
+ "ko-negative-pledge-repeat": "측정값은 인과관계를 주장하지 않음.\n두 공정의 차이는 최종 결론이 아님.",
56
+ "en-negative-pledge": "This is not intended as a head-to-head benchmark."
54
57
  },
55
58
  "negative": {
56
59
  "ko-bold-deliverable-label": "표 7의 출처는 각주 2에 적었다.",
@@ -103,7 +106,10 @@
103
106
  "en-chatbot-opener": "The overview starts with the sample period.",
104
107
  "en-testament": "The project used two review rounds.",
105
108
  "en-left-in-narration": "The file contains the updated overview.",
106
- "en-chatbot-closer": "The requested revision is attached."
109
+ "en-chatbot-closer": "The requested revision is attached.",
110
+ "ko-negative-pledge": "이 장비는 방폭 구역에서 사용하지 않습니다. 냉각수로 쓰지 않음.",
111
+ "ko-negative-pledge-repeat": "첨가제 B는 수율을 개선하지 않았다. 이 결과는 우연이 아니다.",
112
+ "en-negative-pledge": "This is not intended as a substitute for professional advice."
107
113
  },
108
114
  "contextCases": {
109
115
  "plainSourceAttached": [
@@ -16,7 +16,10 @@ This original category comes from the 26 real lines in fixtures/pos-real.txt and
16
16
  - en-demo-self-disclaimer — en, block. A demonstration label attached to a synthesis, assumption or recommendation is process framing. Name what is proposed and what was measured.
17
17
  - ko-bracket-status-tag and en-bracket-status-tag — ko/en, block. Replace bracket keys with a sentence describing the status.
18
18
  - ko-standalone-scope-disclaimer and en-standalone-scope-disclaimer — ko/en, block for stock disclaimers. Preserve a user-requested legal, medical or safety notice and place it once where it applies.
19
- - ko-status-negation, ko-mi-status, en-epistemic-negation and en-not-performed — warnings. A negative result or unperformed action may be the finding. Write the actor, scope and verb plainly; never turn unknown into false or an estimate into a fact.
19
+ - ko-status-negation, ko-mi-status, en-epistemic-negation and en-not-performed — warnings. ko-status-negation also reads 쓰지 않음, the polite 않습니다/않았습니다 endings and a bold-wrapped ending, so a method line such as “fitting parameter는 쓰지 않음” is advised on, never refused. A negative result or unperformed action may be the finding. Write the actor, scope and verb plainly; never turn unknown into false or an estimate into a fact.
20
+ - ko-negative-pledge — ko, block, one line at a time. The reader meets a sentence about what the document is not for: a self-reference (이 표, 본 자료, 해당 보고서) whose verb is negated 해석/의미/주장/대표 or whose noun is a claim noun with 아님; a bare “비교로 쓰지 않음” with no subject; or a purpose word such as 공개용 수치가 아님. Move the sentence to the reply, or turn it into a fact about the data beside the number it affects (“샘플은 한 공정 조건만 포함한다”). Operating rules (사용하지 않습니다, 냉각수로 쓰지 않음), data-handling rules, and legal or financial notices (대체하지, 보장하지, 권유가 아닙니다) stay outside the pattern on purpose.
21
+ - ko-negative-pledge-repeat — ko, block, whole document. Two or more sentences ending in 해석하지/주장하지/대표하지 않음, or in a claim noun + 아님, make a list of disclaimers. One such sentence stays with the warning above; the use verbs and 의미하/간주하/인용하 are left out because definitions and processing rules end that way.
22
+ - en-negative-pledge — en, block, one sentence. “This is not intended as a benchmark”, “these figures do not constitute a comparison”, “not to be read as a ranking”, and “the deck makes no claim about …” say what the document is not. A comparison, benchmark or ranking noun is required, and substitute-for-advice and investment, legal or medical advice notices stay clean. Say it in the reply, or state the data fact.
20
23
  - ko-plain-meta-label-warn and en-plain-caveat-label — warnings for boundary headings. They may be useful in internal or technical packets.
21
24
  - en-knowledge-cutoff, en-ai-self-disclaimer, en-hope-helps and ko-chatbot-signoff — block. Remove the model wrapper or generic sign-off, then retain the answer.
22
25
  - ko-chatbot-opener and en-chatbot-opener — block stock greetings and praise at the start of a deliverable.
@@ -185,7 +185,7 @@
185
185
  "id": "ko-status-negation",
186
186
  "lang": "ko",
187
187
  "severity": "warn",
188
- "pattern": "(?:검증|실측|측정|실행|실시|시작|확인|보장|평가|판단|조언|의미|증명|재현|사용|제공|적용|쓰)(?:[된한]\\s*(?:적이|값이)?|하지(?:\\s*않(?:음|는다|았다))?|되지\\s*않(?:음|는다|았다)|이|은|는)?\\s*(?:아님|않음|않는다|않았다|없음)|보류",
188
+ "pattern": "(?:검증|실측|측정|실행|실시|시작|확인|보장|평가|판단|조언|의미|증명|재현|사용|제공|적용|쓰)(?:[된한](?:\\s*(?:적이|값이))?|하지(?:\\s*않(?:음|는다|았다))?|되지\\s*않(?:음|는다|았다)|(?<=쓰)지|이|은|는)?\\s*(?:(?:\\*\\*|__)\\s*)?(?:아님|않음|않는다|않았다|않습니다|않았습니다|없음)|보류",
189
189
  "flags": "im",
190
190
  "scope": "line",
191
191
  "context": [
@@ -879,6 +879,57 @@
879
879
  "en-chatbot-closer:positive",
880
880
  "en-chatbot-closer:negative"
881
881
  ]
882
+ },
883
+ {
884
+ "id": "ko-negative-pledge",
885
+ "lang": "ko",
886
+ "severity": "block",
887
+ "pattern": "(?<![가-힣])(?:이|본|해당|위|아래)\\s+(?:자료|문서|보고서|슬라이드|덱|표|그림|그래프|차트|수치|결과|비교|분석|내용|예시)(?:은|는)\\s[^.!?\\n]{0,40}?(?:(?:해석하|의미하|주장하|대표하)(?:지\\s*(?:[*_]{1,2}\\s*)?(?:않(?:음|는다|습니다)|말\\s*것|마세요|마십시오)|(?:으)?면\\s*(?:[*_]{1,2}\\s*)?안\\s*(?:됨|된다|됩니다))|(?:(?:비교|결론|벤치마크|순위|대표값|일반화)(?:이|가)|목적이|위한\\s*것이)\\s*(?:[*_]{1,2}\\s*)?(?:아님|아니다|아닙니다)(?![가-힣]))|(?:비교|결론|벤치마크|순위|대표값|일반화)(?:으로|로)\\s*(?:[*_]{1,2}\\s*)?(?:쓰|사용하|해석하|읽|간주하|인용하)(?:지\\s*(?:[*_]{1,2}\\s*)?(?:않(?:음|는다|습니다)|말\\s*것|마세요|마십시오)|(?:으)?면\\s*(?:[*_]{1,2}\\s*)?안\\s*(?:됨|된다|됩니다))|(?:공개|배포|대외|외부|홍보|발표|보고|비교|인용|공식)용\\s*(?:자료|문서|수치|표|그래프)(?:이|가)\\s*(?:[*_]{1,2}\\s*)?(?:아님|아니다|아닙니다)(?![가-힣])",
888
+ "flags": "m",
889
+ "scope": "line",
890
+ "context": [
891
+ "fenced_code",
892
+ "quoted_user_text"
893
+ ],
894
+ "rationale": "A sentence that tells the reader what the document, table or number is not for belongs in the reply or becomes a data fact. Three shapes: a self-reference topic (이/본/해당/위/아래 + document noun + 은/는) with a negated claim verb or a claim noun + 아님 within one bullet; a comparison or ranking noun + (으)로 + a negated use verb; a purpose-용 data noun (공개용 수치) + 아님. Use verbs after a self-reference, 근거/평가/증거/추천, and legal or financial notices (대체하, 보장하, 권유) stay outside the pattern because operating rules, method lines and standard notices use them.",
895
+ "fixture_ids": [
896
+ "ko-negative-pledge:positive",
897
+ "ko-negative-pledge:negative"
898
+ ]
899
+ },
900
+ {
901
+ "id": "ko-negative-pledge-repeat",
902
+ "lang": "ko",
903
+ "severity": "block",
904
+ "pattern": "(?:(?:해석하|주장하|대표하)지\\s*(?:[*_]{1,2}\\s*)?않(?:음|습니다)|(?:비교|결론|벤치마크|순위|대표값|일반화)(?:이|가)\\s*(?:[*_]{1,2}\\s*)?(?:아님|아닙니다)(?![가-힣]))(?:[*_]{1,2})?(?=\\s*(?:[.!?。]|$))[\\s\\S]*?(?:(?:해석하|주장하|대표하)지\\s*(?:[*_]{1,2}\\s*)?않(?:음|습니다)|(?:비교|결론|벤치마크|순위|대표값|일반화)(?:이|가)\\s*(?:[*_]{1,2}\\s*)?(?:아님|아닙니다)(?![가-힣]))(?:[*_]{1,2})?(?=\\s*(?:[.!?。]|$))",
905
+ "flags": "m",
906
+ "scope": "document",
907
+ "context": [
908
+ "fenced_code",
909
+ "quoted_user_text"
910
+ ],
911
+ "rationale": "Two or more sentences that end in 해석하지/주장하지/대표하지 않음 or a claim noun + 이/가 + 아님 turn a document into a list of disclaimers. 의미하, 간주하, 인용하 and the use verbs are left out because repeated definitions, data-processing rules, method lines and operating rules end that way; a single such sentence stays with the warn-only ko-status-negation.",
912
+ "fixture_ids": [
913
+ "ko-negative-pledge-repeat:positive",
914
+ "ko-negative-pledge-repeat:negative"
915
+ ]
916
+ },
917
+ {
918
+ "id": "en-negative-pledge",
919
+ "lang": "en",
920
+ "severity": "block",
921
+ "pattern": "\\b(?:this|these)\\s+(?:deck|report|document|analysis|table|chart|figure|figures|numbers|results|slide|comparison)\\s+(?:(?:is|are)\\s+not\\s+(?:intended|meant|to\\s+be\\s+(?:read|used|taken|interpreted))(?:\\s+to\\s+be\\s+(?:read|used|taken|interpreted))?\\s+as\\b(?!\\s*(?:a\\s+substitute|a\\s+replacement|(?:legal|investment|medical|professional)\\s+advice))\\s+(?:an?\\s+|the\\s+)?(?:[A-Za-z0-9-]+\\s+){0,4}?(?:comparison|benchmark|ranking)s?\\b(?!-)(?!\\s+(?:arm|arms|group|groups|standard|standards|class|baseline)\\b)|(?:should|must)\\s+not\\s+be\\s+(?:read|used|taken|interpreted)\\s+as\\s+(?:an?\\s+|the\\s+)?(?:[A-Za-z0-9-]+\\s+){0,4}?(?:comparison|benchmark|ranking)s?\\b(?!-)(?!\\s+(?:arm|arms|group|groups|standard|standards|class|baseline)\\b)|makes?\\s+no\\s+claims?\\s+(?:about|that|regarding|as\\s+to)\\b)|\\b(?:this|these)\\s+(?:is|are)\\s+not\\s+(?:intended|meant)\\s+as\\b(?!\\s*(?:a\\s+substitute|a\\s+replacement|(?:legal|investment|medical|professional)\\s+advice))\\s+(?:an?\\s+|the\\s+)?(?:[A-Za-z0-9-]+\\s+){0,4}?(?:comparison|benchmark|ranking)s?\\b(?!-)(?!\\s+(?:arm|arms|group|groups|standard|standards|class|baseline)\\b)|\\bthe\\s+(?:deck|report|document|analysis)\\s+makes?\\s+no\\s+claims?\\s+(?:about|that|regarding|as\\s+to)\\b|(?:^|\\s[-*]\\s)\\s*(?:[-*]\\s+|\\d+\\.\\s+)?(?:[*_]{1,2})?not\\s+(?:intended|meant|to\\s+be\\s+read|to\\s+be\\s+used)\\s+as\\b(?!\\s*(?:a\\s+substitute|a\\s+replacement|(?:legal|investment|medical|professional)\\s+advice))\\s+(?:an?\\s+|the\\s+)?(?:[A-Za-z0-9-]+\\s+){0,4}?(?:comparison|benchmark|ranking)s?\\b(?!-)(?!\\s+(?:arm|arms|group|groups|standard|standards|class|baseline)\\b)|\\b(?:this|these)\\s+(?:table|chart|figure|figures|numbers|results|comparison|analysis)\\s+(?:does|do)\\s+not\\s+constitute\\s+(?:an?\\s+)?[^.!?]{0,60}?\\b(?:comparison|benchmark|ranking)s?\\b(?!-)(?!\\s+(?:arm|arms|group|groups|standard|standards|class|baseline)\\b)",
922
+ "flags": "i",
923
+ "scope": "sentence",
924
+ "context": [
925
+ "fenced_code",
926
+ "quoted_user_text"
927
+ ],
928
+ "rationale": "A sentence that says the deck, report or numbers are not intended, meant or to be read as a comparison, benchmark or ranking, or make no claim about something belongs in the reply, and so does a table, chart or set of numbers that does not constitute one. Requiring one of those nouns keeps safety labels and advice, offer or diagnosis notices clean; \"does not constitute\" for a report or document stays a notice form, and \"the\" is not a subject except with \"makes no claim\".",
929
+ "fixture_ids": [
930
+ "en-negative-pledge:positive",
931
+ "en-negative-pledge:negative"
932
+ ]
882
933
  }
883
934
  ]
884
935
  }
@@ -92,7 +92,7 @@ Under `lit`, create a `.pptx` by default. Do not ask a theme, colour or font que
92
92
 
93
93
  4. **Give each message the visual that fits it, sized to the slide.** A numeric series is a native editable chart (`::: chart type=column|bar|line|area|stacked|pie|doughnut`, a pipe table of categories and series, optional `unit=` and a `>` caption; one series carries its values as labels, while lines of two or more series are read off a shown value axis, so keep a line chart to the series that matter); headline numbers are a `::: kpi-table` row of four to six figures with a second row giving each figure's basis or comparison; a sequence is a process or timeline family; exact lookups are a table of five to eight rows with right-aligned numbers; options are a comparison with three or four labelled criteria rows. Numbers never become the display element: no figure is set above the title size, and nothing is inflated or rounded up for effect. A title and a small table floating over half an empty slide is unfinished.
94
94
 
95
- 5. **Write dense, sourced content.** Every pack defaults to density 10, the compact step: 24 pt margins, body 13 pt, table cells 11 pt, sources 9 pt, never below those floors. Each content slide carries its basis, a comparison, the period, and an implication or next action; data slides end with a `출처:` / `Source:` line (and a `주:` / `Note:` line where a definition matters), which the engine sets as a strip on the body floor. References run eight to ten entries. `references/density-and-fill.md` lists what each family needs to fill its body and what the fill policies do when it does not.
95
+ 5. **Write dense, sourced content.** Every pack defaults to density 10, the compact step: 24 pt margins, body 13 pt, table cells 11 pt, sources 9 pt, never below those floors. Each content slide carries its basis, a comparison, the period, and an implication or next action; data slides end with a `출처:` / `Source:` line (and a `주:` / `Note:` line where a definition matters), which the engine sets as a strip on the body floor. Write a real uncertainty once, as a fact about the data or method beside the number it affects; what the deck is not for goes in the reply. References run eight to ten entries. `references/density-and-fill.md` lists what each family needs to fill its body and what the fill policies do when it does not.
96
96
 
97
97
  6. **Write the Markdown source** as `<name>.md` beside the output, with `tonality:`, `title:` and, for sample content, `notice:` in its frontmatter. `specs/markdown-slide-spec-v2.md` is the dialect contract; open the worked example in `references/examples/` closest to the job before writing. Twelve examples cover all eight tonalities in Korean and English and each compiles and passes the gate as shipped. Pictures are referenced by path from the source folder; the skill ships no images. For a chart or diagram picture on a dark tonality, put its dark variant beside it (`flow.png` and `flow.dark.png`); a dark ground draws the variant. Korean text runs and charts with Korean labels are written as ko-KR, so a renderer applies Korean spacing rules.
98
98
 
@@ -102,7 +102,7 @@ Under `lit`, create a `.pptx` by default. Do not ask a theme, colour or font que
102
102
 
103
103
  9. **Fonts for travel.** When the deck must open on machines without Pretendard, run `node <skill>/run.mjs embed deck.pptx` and rerun the gate. The post-embedding integrity check reopens the deck and verifies ZIP data, content types and relationship targets.
104
104
 
105
- 10. **Look at the pages.** Render with LibreOffice when available: `soffice -env:UserInstallation=file:///tmp/<unique-profile> --headless --convert-to pdf --outdir <output-dir> deck.pptx`, convert the PDF to PNG, and open every slide at presentation size. Check hierarchy, Hangul line breaks, numbers against the source, cramped or hollow areas, and whether each treatment suits its slide. Check the card's "wrong if" line: if it came true, rebuild under the alternative and update the card and reply. Three full compile → gate → look rounds is the budget; a defect that survives is reported as a limitation. Without soffice, report that structural QA ran and visual preview was unavailable; never claim render verification.
105
+ 10. **Look at the pages.** Render with LibreOffice when available: `soffice -env:UserInstallation=file:///tmp/<unique-profile> --headless --convert-to pdf --outdir <output-dir> deck.pptx`, convert the PDF to PNG, and open every slide at presentation size. Check hierarchy, Hangul line breaks, numbers against the source, cramped or hollow areas, and whether each treatment suits its slide. Check the card's "wrong if" line: if it came true, rebuild under the alternative and update the card and reply. Three full compile → gate → look rounds is the budget; a defect that survives is named in the reply, not written onto a slide. Without soffice, report that structural QA ran and visual preview was unavailable; never claim render verification.
106
106
 
107
107
  11. **Deliver.** The `.pptx`, its Markdown source, the build log with the direction card and, when useful, the rendered PNGs sit in the user's project. The reply names the tonality and the two alternatives. Never leave generated files inside the skill.
108
108
 
@@ -236,7 +236,7 @@ Enrolled templates may advertise these semantic compositions of existing blocks:
236
236
 
237
237
  - **figure plus interpretation:** `image` + `figure-caption` + body interpretation;
238
238
  - **table plus decision takeaway:** table/`kpi-table` + `table-caption` + body takeaway;
239
- - **source capture:** `image` + `figure-caption` containing locator/source + body limitation/relevance;
239
+ - **source capture:** `image` + `figure-caption` containing locator/source + body stating what the source shows and why it is on the slide;
240
240
  - **two-record appendix evidence:** title/body or `summary-group`, with no more than two records and DOI/canonical links when available.
241
241
 
242
242
  For a Markdown image used as evidence, alt text is the visible figure caption and should contain a figure number plus `Source:`/`출처:`. A blockquote immediately after a table is its visible table caption/source. Appendix DOI/canonical/raw URL fields use `[label](https://...)` in table cells so the PPTX contains an external hyperlink relationship. These requirements reuse existing parser and renderer behavior.
@@ -245,7 +245,7 @@
245
245
  },
246
246
  {
247
247
  "path": "fixtures/rule-cases.json",
248
- "sha256": "309e65f6de8ebacd98651d55f1cc946f6c2be9f81187d721de25ef522e6be9f9"
248
+ "sha256": "498dae0a7aa89ec1b8b6b5bcd929a099c2fc988fbf4d18598f638cee16eeace4"
249
249
  },
250
250
  {
251
251
  "path": "references/code-patterns.md",
@@ -293,11 +293,11 @@
293
293
  },
294
294
  {
295
295
  "path": "references/taxonomy.md",
296
- "sha256": "82d8e806a7aa800e2fee106f0959b7b689d5e92e4b1356f259e8a5dfefb6dcde"
296
+ "sha256": "12762da55de333e180cdb55e1e1964d43cad2d2f355ea789e7f82fda0a06a2d9"
297
297
  },
298
298
  {
299
299
  "path": "rules.json",
300
- "sha256": "d69ad386d534bde0fdbc08603151f82477db78e4c920203bf070128e927a7a5b"
300
+ "sha256": "6153ca0835b04fc9512c903b4c5080c3a45cd1fd68c1a9dbad8972b5a150fb6c"
301
301
  },
302
302
  {
303
303
  "path": "scripts/core.mjs",
@@ -3636,7 +3636,7 @@
3636
3636
  },
3637
3637
  {
3638
3638
  "path": "specs/markdown-slide-spec-v1.md",
3639
- "sha256": "eb9b04d070d9f2774746e65a34e5e6833fefd097ea7e01fc32327934ca8ee42b"
3639
+ "sha256": "90ab86cef7ac7d7b8fa2cbe5b309c455638948272d84508fdf35aaab635d31bc"
3640
3640
  },
3641
3641
  {
3642
3642
  "path": "specs/markdown-slide-spec-v2.md",
@@ -3831,7 +3831,7 @@
3831
3831
  },
3832
3832
  {
3833
3833
  "path": "references/tonalities/manual.md",
3834
- "sha256": "c065f438f98f105e25a512308727c9fda542ac4ab70e9f3f92979745bbb7838f"
3834
+ "sha256": "db39dcc2f02cf2931286bb71ee20cc8c6ff551f6b618e845c024114527f1d5ee"
3835
3835
  },
3836
3836
  {
3837
3837
  "path": "references/tonalities/memo.md",