@litfamily/litgrok 1.0.22 → 1.0.24

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.
@@ -36,21 +36,90 @@ function field(value, pattern) {
36
36
  return stringLeaves(value).find(({ key }) => pattern.test(key))?.value;
37
37
  }
38
38
 
39
- function changedText(input) {
39
+ const MAX_FILE_BYTES = 2 * 1024 * 1024;
40
+ const FENCE = /^ {0,3}(?:`{3,}|~{3,})/;
41
+
42
+ // Line-level diff of an Edit: the 1-based lines of `after` that differ from `before`.
43
+ function changedLines(before, after, offset) {
44
+ const old = before.split('\n');
45
+ const next = after.split('\n');
46
+ let head = 0;
47
+ while (head < old.length && head < next.length && old[head] === next[head]) head += 1;
48
+ let tail = 0;
49
+ while (tail < old.length - head && tail < next.length - head && old[old.length - 1 - tail] === next[next.length - 1 - tail]) tail += 1;
50
+ let first = head;
51
+ let end = next.length - tail;
52
+ // A pure deletion can join its two neighbours into one paragraph.
53
+ if (end <= first) { first = Math.max(0, head - 1); end = Math.min(next.length, head + 1); }
54
+ const lines = new Set();
55
+ for (let index = first; index < end; index += 1) lines.add(offset + index + 1);
56
+ return lines;
57
+ }
58
+
59
+ function readCurrent(file, cwd) {
60
+ try {
61
+ const target = resolve(cwd, file);
62
+ const stat = lstatSync(target);
63
+ if (!stat.isFile() || stat.size > MAX_FILE_BYTES) return null;
64
+ return readFileSync(target, 'utf8');
65
+ } catch { return null; }
66
+ }
67
+
68
+ // Rebuild what a patch's hunks show: context and added lines in order, a blank line at every
69
+ // hunk or file break, removed lines dropped. The added lines are the changed ones.
70
+ function patchView(patch) {
71
+ const rows = [];
72
+ const changed = new Set();
73
+ const gap = () => { if (rows.length && rows[rows.length - 1] !== '') rows.push(''); };
74
+ for (const line of patch.split(/\r?\n/)) {
75
+ if (/^(?:\+\+\+|---) |^\*\*\*|^@@|^diff |^index /.test(line)) gap();
76
+ else if (line.startsWith('+')) { rows.push(line.slice(1)); changed.add(rows.length); }
77
+ else if (line.startsWith(' ')) rows.push(line.slice(1));
78
+ else if (line === '') rows.push('');
79
+ else if (!line.startsWith('-') && !line.startsWith('\\')) gap();
80
+ }
81
+ return { text: rows.join('\n'), changed };
82
+ }
83
+
84
+ // The text to scan and the 1-based lines of it that this write changes. An Edit is applied to the
85
+ // current file so fences, quotes and paragraph breaks keep their context; without a readable file
86
+ // the new_string is scanned whole with its own fence state.
87
+ function writeView(input, cwd) {
40
88
  const before = field(input, OLD_KEY);
41
89
  const after = field(input, /^new_?string$/i);
42
90
  if (before !== undefined && after !== undefined) {
43
- let start = 0;
44
- while (start < before.length && start < after.length && before[start] === after[start]) start += 1;
45
- let endBefore = before.length;
46
- let endAfter = after.length;
47
- while (endBefore > start && endAfter > start && before[endBefore - 1] === after[endAfter - 1]) { endBefore -= 1; endAfter -= 1; }
48
- return after.slice(start, endAfter);
91
+ const file = field(input, PATH_KEY);
92
+ const current = file && before ? readCurrent(file, cwd) : null;
93
+ const at = current === null ? -1 : current.indexOf(before);
94
+ if (at < 0) return { text: after, changed: changedLines(before, after, 0) };
95
+ const text = current.slice(0, at) + after + current.slice(at + before.length);
96
+ return { text, changed: changedLines(before, after, current.slice(0, at).split('\n').length - 1) };
49
97
  }
50
98
  const leaves = stringLeaves(input).filter(({ key }) => CONTENT_KEY.test(key) && !OLD_KEY.test(key));
51
99
  const patch = leaves.find(({ key }) => /^patch$/i.test(key))?.value;
52
- if (patch) return patch.split(/\r?\n/).filter((line) => /^\+(?!\+\+)/.test(line)).map((line) => line.slice(1)).join('\n');
53
- return leaves.map(({ value }) => value).join('\n');
100
+ if (patch) return patchView(patch);
101
+ // A Write over a readable file is judged on the lines it changes; a new file is judged whole.
102
+ const text = leaves.map(({ value }) => value).join('\n');
103
+ const file = field(input, PATH_KEY);
104
+ const current = file ? readCurrent(file, cwd) : null;
105
+ return { text, changed: current === null ? null : changedLines(current, text, 0) };
106
+ }
107
+
108
+ // The 1-based lines a finding covers. A counted rule reads a whole paragraph, so its finding covers it.
109
+ // A document-scope match covers the lines where it starts and ends (its first and last occurrence),
110
+ // not the lines between them.
111
+ function findingLines(finding, lines, counted, documentScope) {
112
+ if (documentScope && !counted) return [...new Set([finding.line, finding.endLine ?? finding.line])];
113
+ let first = finding.line;
114
+ let last = finding.line;
115
+ if (counted) {
116
+ const prose = (number) => number >= 1 && number <= lines.length && lines[number - 1].trim() && !FENCE.test(lines[number - 1]);
117
+ while (prose(first - 1)) first -= 1;
118
+ while (prose(last + 1)) last += 1;
119
+ }
120
+ const covered = [];
121
+ for (let number = first; number <= last; number += 1) covered.push(number);
122
+ return covered;
54
123
  }
55
124
 
56
125
  function readerFacing(file) {
@@ -59,7 +128,8 @@ function readerFacing(file) {
59
128
 
60
129
  function removeQuotedPrompt(text, event) {
61
130
  for (const { key, value } of stringLeaves(event).filter(({ key }) => PROMPT_KEY.test(key))) {
62
- if (value.length > 0) text = text.split(value).join(' ');
131
+ // Keep the line breaks so changed-line numbers still match.
132
+ if (value.length > 0) text = text.split(value).join(value.replace(/[^\n]+/g, ' '));
63
133
  }
64
134
  return text;
65
135
  }
@@ -67,8 +137,9 @@ function removeQuotedPrompt(text, event) {
67
137
  function writeTarget(event) {
68
138
  const file = field(event.toolInput, PATH_KEY);
69
139
  if (!file || file.includes('\n') || !readerFacing(file)) return null;
70
- const text = removeQuotedPrompt(changedText(event.toolInput), event);
71
- return text.trim() ? { file, text } : null;
140
+ const view = writeView(event.toolInput, event.cwd || process.cwd());
141
+ const text = removeQuotedPrompt(view.text, event);
142
+ return text.trim() ? { file, text, changed: view.changed } : null;
72
143
  }
73
144
 
74
145
  function postTargets(event) {
@@ -114,14 +185,28 @@ try {
114
185
  const target = writeTarget(event);
115
186
  if (!target) allow();
116
187
  else {
117
- const findings = scanText(target.text, loadRules(), target.file);
188
+ const rules = loadRules();
189
+ const lines = target.text.split('\n');
190
+ const counted = new Set(rules.filter((rule) => rule.threshold !== undefined).map((rule) => rule.id));
191
+ const documentScope = new Set(rules.filter((rule) => rule.scope === 'document').map((rule) => rule.id));
192
+ const findings = scanText(target.text, rules, target.file).filter((item) => {
193
+ if (!target.changed) return true;
194
+ return findingLines(item, lines, counted.has(item.rule), documentScope.has(item.rule)).some((number) => target.changed.has(number));
195
+ });
118
196
  const finding = findings.find((item) => item.severity === 'block');
197
+ const advice = rules.find((rule) => rule.id === 'negation-cluster')?.message;
119
198
  if (!finding) {
120
199
  const warning = findings.find((item) => item.severity === 'warn');
121
- allow(warning ? `advisory ${warning.rule} in ${target.file}; review wording` : undefined);
200
+ const message = advice && findings.some((item) => item.rule === 'negation-cluster');
201
+ allow(warning ? `advisory ${warning.rule} in ${target.file}; review wording${message ? `: ${advice}` : ''}` : undefined);
122
202
  }
123
203
  else {
124
- process.stdout.write(`${JSON.stringify({ decision: 'deny', reason: `lit-humanizer ${blockNote(target.file, finding)}` })}\n`);
204
+ // The deny reason carries the rewrite advice when a negation cluster is what blocks.
205
+ const message = advice && findings.some((item) => item.rule === 'negation-cluster' && item.severity === 'block');
206
+ const reason = `lit-humanizer ${blockNote(target.file, finding)}${message ? `: ${advice}` : ''}`;
207
+ process.stdout.write(`${JSON.stringify({ decision: 'deny', reason })}\n`);
208
+ // Grok Build reads the JSON reason; its first stderr line is the fallback reason on exit 2.
209
+ process.stderr.write(`${reason.replace(/\s*\n\s*/g, ' ')}\n`);
125
210
  process.exitCode = 2;
126
211
  }
127
212
  }
@@ -27,6 +27,10 @@ Use the always-on rule for ordinary turns. Load this full skill for a long deliv
27
27
  4. **Re-check.** Run `scripts/detect.mjs` on the newly written text. Fix every block hit. Review warnings against context and keep a warning when it is accurate, useful, and natural. Re-run until block hits are zero and the remaining warnings are deliberate.
28
28
  5. **Deliver.** Return the requested artifact without an audit preamble or process labels. In the chat reply, summarize the change only if useful and state any material risk once, plainly.
29
29
 
30
+ ## Positive-first wording
31
+
32
+ Lead with what a number or thing is, then add a needed caveat once. `18,420회는 주민 수가 아님. 고유 방문자 수도 아님.` leaves the reader to guess what the figure counts; `18,420회는 같은 사람의 반복 방문을 포함한 방문 횟수 합계입니다` says it, and the caveat is no longer needed. The detector's `negation-cluster` rule counts definitional negations in each paragraph, list block or table column: the plain forms (`아닙니다`, `아님`, `아니라`, `is not`, `rather than` and the like) and the reworded ones that say the same thing (`~로 읽을 수 없음`, `~로 보지 않음`, `~을 뜻하지 않음`, `~와는 다름`, `~로 읽으면 안 됨`, `should not be read as`, `does not mean`). Two Korean matches, or three in any language, block the write, and the refusal carries the rewrite advice; exactly two with at most one Korean match draw a warning. Trading `아님` for `~로 읽을 수 없음` leaves the count unchanged. The count skips code, quotes and table cells of 12 characters or fewer, and one honest limitation line such as `고유 이용자 지표는 자료에 없습니다` stays clean. Put any bad example you write in inline code or a blockquote.
33
+
30
34
  ## Modes
31
35
 
32
36
  - **Fast:** For short replies and small edits. Read once, revise clear residue, preserve the original voice, then check the changed text.
@@ -2,7 +2,9 @@
2
2
 
3
3
  Apply to new text you write in chat or files. Check only new or changed model text; preserve existing file text and exact user quotations. Skip code fences, inline code, and internal records. Load the full skill for long deliverables, explicit rewrites, or Korean deep work.
4
4
 
5
- Block only clear drafting residue: evidence/source label lines, stacked caveats, model disclaimers, and chatbot sign-offs. Warn on vocabulary, contrast, triads, and rhythm; revise only when voice and genre improve.
5
+ Block only clear drafting residue: evidence/source label lines, stacked caveats, stacked definitional negations, model disclaimers, and chatbot sign-offs. Warn on vocabulary, contrast, triads, and rhythm; revise only when voice and genre improve.
6
+
7
+ State what a number or thing is first and add a needed caveat once: rewrite `18,420회는 주민 수가 아님` as "18,420회는 같은 사람의 반복 방문을 포함한 방문 횟수 합계입니다". The guard blocks a paragraph, list or table column that stacks two Korean definitional negations or three in any language, and counts reworded forms such as `~로 읽을 수 없음` the same way; exactly two that are not both Korean draw a warning.
6
8
 
7
9
  Use footnotes or reference lists for citations, never label-only lines. State material risk once, plainly, in chat, not in the deliverable body. Keep internal evidence, plans, ledgers, handoffs, status, and JSON detailed.
8
10
 
@@ -53,7 +53,8 @@
53
53
  "en-chatbot-closer": "Let me know if you would like another version.",
54
54
  "ko-negative-pledge": "동일 조건의 독립 성능 비교로 쓰지 않음.",
55
55
  "ko-negative-pledge-repeat": "측정값은 인과관계를 주장하지 않음.\n두 공정의 차이는 최종 결론이 아님.",
56
- "en-negative-pledge": "This is not intended as a head-to-head benchmark."
56
+ "en-negative-pledge": "This is not intended as a head-to-head benchmark.",
57
+ "negation-cluster": "방문 횟수임. 같은 사람의 재방문이 섞여 있어 이용자 수로 읽을 수 없음. 고유 방문자 수도 아님."
57
58
  },
58
59
  "negative": {
59
60
  "ko-bold-deliverable-label": "표 7의 출처는 각주 2에 적었다.",
@@ -109,7 +110,8 @@
109
110
  "en-chatbot-closer": "The requested revision is attached.",
110
111
  "ko-negative-pledge": "이 장비는 방폭 구역에서 사용하지 않습니다. 냉각수로 쓰지 않음.",
111
112
  "ko-negative-pledge-repeat": "첨가제 B는 수율을 개선하지 않았다. 이 결과는 우연이 아니다.",
112
- "en-negative-pledge": "This is not intended as a substitute for professional advice."
113
+ "en-negative-pledge": "This is not intended as a substitute for professional advice.",
114
+ "negation-cluster": "18,420회는 같은 사람의 반복 방문을 포함한 방문 횟수 합계입니다. 고유 방문자 수는 자료에 없습니다."
113
115
  },
114
116
  "contextCases": {
115
117
  "plainSourceAttached": [
@@ -934,6 +934,30 @@
934
934
  "en-negative-pledge:positive",
935
935
  "en-negative-pledge:negative"
936
936
  ]
937
+ },
938
+ {
939
+ "id": "negation-cluster",
940
+ "lang": "any",
941
+ "severity": "block",
942
+ "pattern": "아닙니다|아님|아니다|아니며|아니라|아닌|(?:로|으로) (?:읽을|볼|해석할|간주할) 수 없|(?:로|으로) 보지 않|(?:을|를) (?:뜻하지|의미하지|나타내지|가리키지) 않|(?:와|과)는 다(?:릅니다|르다|름|르며|르고|르므로)|(?:로|으로) (?:읽으면|보면|해석하면) 안 |(?<![\\p{L}\\p{N}_])(?:is not|are not|isn't|aren't|not a|rather than)(?![\\p{L}\\p{N}_])|(?<![\\p{L}\\p{N}_])(?:should not|cannot|can't|must not) be (?:read|taken|interpreted|treated) as(?![\\p{L}\\p{N}_])|(?<![\\p{L}\\p{N}_])(?:does|do) not (?:mean|represent|indicate)(?![\\p{L}\\p{N}_])",
943
+ "ko_pattern": "아닙니다|아님|아니다|아니며|아니라|아닌|(?:로|으로) (?:읽을|볼|해석할|간주할) 수 없|(?:로|으로) 보지 않|(?:을|를) (?:뜻하지|의미하지|나타내지|가리키지) 않|(?:와|과)는 다(?:릅니다|르다|름|르며|르고|르므로)|(?:로|으로) (?:읽으면|보면|해석하면) 안 ",
944
+ "flags": "iu",
945
+ "scope": "document",
946
+ "threshold": {
947
+ "block_ko": 2,
948
+ "block_total": 3,
949
+ "warn_total": 2
950
+ },
951
+ "context": [
952
+ "fenced_code",
953
+ "quoted_user_text"
954
+ ],
955
+ "message": "Several negations sit in one place. Say what the thing is first, then add the caveat once. Example: write \"18,420 visits in total, including repeat visits\" instead of \"18,420 is not the number of residents\". (한곳에 부정문이 여럿 있습니다. 무엇인지 먼저 말하고, 단서는 한 번만 붙이세요. 예: \"18,420회는 주민 수가 아님\" 대신 \"18,420회는 같은 사람의 반복 방문을 포함한 방문 횟수 합계입니다\".)",
956
+ "rationale": "Counts definitional negations, plain and reworded (~로 읽을 수 없음, ~로 보지 않음, ~와는 다름, ~로 읽으면 안 됨, does not mean), in each paragraph, list block or Markdown table column. Two Korean matches (ko_pattern) or three in any language block the write; exactly two that are not both Korean only warn. Table cells of 12 characters or fewer, such as a 해당 아님 status, are left out. One plain caveat after a positive statement stays clean.",
957
+ "fixture_ids": [
958
+ "negation-cluster:positive",
959
+ "negation-cluster:negative"
960
+ ]
937
961
  }
938
962
  ]
939
963
  }
@@ -1,6 +1,6 @@
1
1
  import path from 'node:path';
2
2
 
3
- const VALID_LANGS = new Set(['ko', 'en', 'code']);
3
+ const VALID_LANGS = new Set(['ko', 'en', 'code', 'any']);
4
4
  const VALID_SEVERITIES = new Set(['block', 'warn']);
5
5
  const VALID_SCOPES = new Set(['line', 'sentence', 'document']);
6
6
 
@@ -19,6 +19,15 @@ export function parseRules(raw) {
19
19
  if (typeof rule.pattern !== 'string' || typeof rule.flags !== 'string' || !Array.isArray(rule.context) || !Array.isArray(rule.fixture_ids)) {
20
20
  throw new Error(`incomplete rule definition: ${rule.id}`);
21
21
  }
22
+ if (rule.threshold !== undefined) {
23
+ const { block_ko: blockKo, block_total: blockTotal, warn_total: warnTotal } = rule.threshold ?? {};
24
+ if (![blockKo, blockTotal, warnTotal].every((value) => Number.isInteger(value) && value >= 2) ||
25
+ warnTotal >= blockTotal || typeof rule.ko_pattern !== 'string' || rule.severity !== 'block') {
26
+ throw new Error(`a counted rule needs block_ko, block_total and warn_total of 2 or more, a ko_pattern and block severity: ${rule.id}`);
27
+ }
28
+ const counter = (pattern) => new RegExp(pattern, `${rule.flags.replace('g', '')}g`);
29
+ return { ...rule, regex: new RegExp(rule.pattern, rule.flags), counter: counter(rule.pattern), koCounter: counter(rule.ko_pattern) };
30
+ }
22
31
  return { ...rule, regex: new RegExp(rule.pattern, rule.flags) };
23
32
  });
24
33
  }
@@ -189,12 +198,57 @@ function ruleSegments(rows, rule, cache) {
189
198
  return segments;
190
199
  }
191
200
 
201
+ // A counted rule (one with a tiered threshold) reads each paragraph or list block on its own, split on blank
202
+ // lines, and a Markdown table one column at a time. Cells of 12 characters or fewer are status values.
203
+ function countedSegments(rows) {
204
+ const blocks = [];
205
+ let group = [];
206
+ for (const row of [...rows, { skipped: true }]) {
207
+ if (!row.skipped && row.candidate.trim()) group.push(row);
208
+ else if (group.length) { blocks.push(group); group = []; }
209
+ }
210
+ const segments = [];
211
+ for (const block of blocks) {
212
+ if (!block.every((row) => /^\s*\|/.test(row.candidate))) {
213
+ segments.push(joinRows(block, '\n'));
214
+ continue;
215
+ }
216
+ const table = block.filter((row) => !/^[\s|:-]+$/.test(row.candidate))
217
+ .map((row) => row.candidate.trim().replace(/^\|+|\|+$/g, '').split('|').map((cell) => cell.trim()));
218
+ const width = table.reduce((most, cells) => Math.max(most, cells.length), 0);
219
+ for (let column = 0; column < width; column += 1) {
220
+ const text = table.map((cells) => cells[column] ?? '').filter((cell) => [...cell].length > 12).join(' ');
221
+ if (text) segments.push({ text, line: block[0].number });
222
+ }
223
+ }
224
+ return segments;
225
+ }
226
+
192
227
  export function scanText(text, rules, file = '<stdin>') {
193
228
  if (isInternalPath(file)) return [];
194
229
  const findings = [];
195
230
  const rows = sourceRows(text);
196
231
  const segmentCache = new Map();
197
232
  for (const rule of rules) {
233
+ if (rule.threshold !== undefined) {
234
+ if (!segmentCache.has('counted')) segmentCache.set('counted', countedSegments(rows));
235
+ // Block at block_ko Korean matches or block_total in any language; warn at exactly warn_total.
236
+ const { block_ko: blockKo, block_total: blockTotal, warn_total: warnTotal } = rule.threshold;
237
+ for (const segment of segmentCache.get('counted')) {
238
+ const matches = [...segment.text.matchAll(rule.counter)];
239
+ const ko = [...segment.text.matchAll(rule.koCounter)].length;
240
+ const severity = ko >= blockKo || matches.length >= blockTotal ? 'block' : matches.length >= warnTotal ? 'warn' : null;
241
+ if (!severity) continue;
242
+ findings.push({
243
+ file,
244
+ rule: rule.id,
245
+ severity,
246
+ line: lineAtOffset(segment, matches[0].index),
247
+ excerpt: segment.text.trim().replace(/\s+/g, ' ').slice(0, 180),
248
+ });
249
+ }
250
+ continue;
251
+ }
198
252
  for (const segment of ruleSegments(rows, rule, segmentCache)) {
199
253
  if (!matchesLanguage(rule, segment.text)) continue;
200
254
  rule.regex.lastIndex = 0;
@@ -205,6 +259,7 @@ export function scanText(text, rules, file = '<stdin>') {
205
259
  rule: rule.id,
206
260
  severity: rule.severity,
207
261
  line: lineAtOffset(segment, match.index),
262
+ endLine: lineAtOffset(segment, match.index + Math.max(0, match[0].length - 1)),
208
263
  excerpt: segment.text.trim().replace(/\s+/g, ' ').slice(0, 180),
209
264
  });
210
265
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "litgrok",
3
- "version": "1.0.22",
3
+ "version": "1.0.24",
4
4
  "description": "Grok Build skills, project rules, and bounded lifecycle hooks for evidence-first lit work.",
5
5
  "author": {
6
6
  "name": "LitGrok contributors"
package/CHANGELOG.md CHANGED
@@ -2,6 +2,18 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 1.0.24 — 2026-10-19
6
+
7
+ - Reports and decks no longer pile up sentences that define something by what it is not. The deliverable guard counts definitional negations such as `아닙니다`, `아님`, `is not` and `rather than` in each paragraph, list block or table column. Two Korean ones, or three in any language, make the `PreToolUse` hook deny the write with advice to say what the thing is first; two that are not both Korean only warn. Reworded forms that restate the same definition, such as `~로 읽을 수 없음`, `~을 뜻하지 않음`, `~와는 다르다`, `~로 읽으면 안 된다`, `should not be read as` and `does not mean`, count too, so swapping one negation for another no longer gets past the guard. A plain comparison such as `A와 B는 다르다` is still allowed.
8
+ - Code, quotes, internal paths and short table cells (12 characters or fewer, counted as characters, so an emoji status cell is skipped) are left out of the count.
9
+ - An Edit, a patch or a Write over an existing file is now judged in the context of the whole file, and only on the lines it changes. A change inside a code fence, a blockquote or inline code, or two paragraphs that each have one negation, is no longer refused by mistake, and a new file is still checked in full. The deny reason also goes to stderr as one line.
10
+ - The `lit-humanizer` skill and the always-on rule describe positive-first wording with a before-and-after example.
11
+
12
+ ## 1.0.23 — 2026-10-18
13
+
14
+ - The GitHub and npm READMEs, in English and Korean, now say in their first lines what changes for a Grok Build user, show a real session as a short sketch, and name the kind of work LitGrok suits. Install comes sooner, and requirements and troubleshooting read as short paragraphs instead of bold-label lists. The images are unchanged.
15
+ - Leftover notes about the retired skill-hint feature are gone from the test that keeps such a hint from being added; the test is now `test/no-external-skill-hint.test.mjs` and still fails if a hint hook, its old switch or a call to its external API appears. No runtime behavior changed.
16
+
5
17
  ## 1.0.22 — 2026-10-18
6
18
 
7
19
  - Asking for a Markdown or plain-text deliverable, such as `report.md` or `slides.md`, now gets plain Markdown. The project rule used to send these to `lit-docx` and `lit-pptx`, so renderer source like front matter and `::: callout` blocks landed in the reader's file and the run spent its time building Office files nobody asked for. Both skills now say their source format is renderer input only. Korean report prose keeps full sentences: the Brief tonality's 개조식 endings apply only when asked for, and the sentence-variance lint is no longer answered by cutting sentences into fragments.
package/README.md CHANGED
@@ -1,20 +1,20 @@
1
- <p align="center"><picture><source media="(prefers-reduced-motion: reduce)" srcset="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.22/docs/assets/cover-motion-still.webp" /><source media="(prefers-reduced-motion: no-preference)" srcset="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.22/docs/assets/cover-motion.webp" /><img src="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.22/docs/assets/cover-motion.webp" width="100%" alt="LitFamily motion cover: five armored robots power on one by one, the LitGrok 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/litgrok@1.0.24/docs/assets/cover-motion-still.webp" /><source media="(prefers-reduced-motion: no-preference)" srcset="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.24/docs/assets/cover-motion.webp" /><img src="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.24/docs/assets/cover-motion.webp" width="100%" alt="LitFamily motion cover: five armored robots power on one by one, the LitGrok robot wakes with glowing eyes and a lit frame, then LITFAMILY and KEEP THE WORK LIT. light up." /></picture></p>
2
2
 
3
3
  <h1 align="center">LitGrok</h1>
4
4
  <p align="center"><strong>Keep the work lit.</strong></p>
5
5
 
6
- Skills, a project rule and hooks for xAI's Grok Build that help every session plan the change, build it, check it, and leave a handoff the next session can pick up.
6
+ LitGrok gives xAI's Grok Build the habit of planning before it edits, checking what it built, and leaving a handoff, so tomorrow's session starts where today's stopped. One npm command adds its skills, project rule and hooks to your project.
7
7
 
8
- **[Full README on GitHub](https://github.com/wjgoarxiv/litgrok#readme)** · [Guide](https://github.com/wjgoarxiv/litgrok/blob/main/docs/guide.md) · [All skills](https://github.com/wjgoarxiv/litgrok/blob/main/docs/skills.md) · [한국어](https://github.com/wjgoarxiv/litgrok/blob/main/README_ko-KR.md) · [Install](#install-in-30-seconds) · [Reference](https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.22/docs/reference.md)
8
+ **[Full README on GitHub](https://github.com/wjgoarxiv/litgrok#readme)** · [Guide](https://github.com/wjgoarxiv/litgrok/blob/main/docs/guide.md) · [All skills](https://github.com/wjgoarxiv/litgrok/blob/main/docs/skills.md) · [한국어](https://github.com/wjgoarxiv/litgrok/blob/main/README_ko-KR.md) · [Install](#install-in-30-seconds) · [Reference](https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.24/docs/reference.md)
9
9
 
10
10
  <p align="center">
11
- <a href="#install-in-30-seconds"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.22/docs/assets/readme/badge-version.svg" alt="1.0.22" /></a>
12
- <a href="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.22/LICENSE"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.22/docs/assets/readme/badge-license.svg" alt="MIT license" /></a>
11
+ <a href="#install-in-30-seconds"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.24/docs/assets/readme/badge-version.svg" alt="1.0.24" /></a>
12
+ <a href="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.24/LICENSE"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.24/docs/assets/readme/badge-license.svg" alt="MIT license" /></a>
13
13
  </p>
14
14
 
15
- <p align="center"><a href="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.22/docs/reference.md"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.22/docs/assets/readme/lucide-book-open.svg" width="16" alt="" /> Docs</a> &nbsp; <a href="#install-in-30-seconds">Install</a> &nbsp; <a href="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.22/docs/assets/cover-motion.webp"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.22/docs/assets/readme/lucide-play.svg" width="16" alt="" /> Cover motion</a> &nbsp; <a href="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.22/LICENSE"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.22/docs/assets/readme/lucide-shield-check.svg" width="16" alt="" /> MIT</a></p>
15
+ <p align="center"><a href="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.24/docs/reference.md"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.24/docs/assets/readme/lucide-book-open.svg" width="16" alt="" /> Docs</a> &nbsp; <a href="#install-in-30-seconds">Install</a> &nbsp; <a href="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.24/docs/assets/cover-motion.webp"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.24/docs/assets/readme/lucide-play.svg" width="16" alt="" /> Cover motion</a> &nbsp; <a href="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.24/LICENSE"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.24/docs/assets/readme/lucide-shield-check.svg" width="16" alt="" /> MIT</a></p>
16
16
 
17
- Long Grok Build sessions get compacted, a new session starts without what the last one knew, and "done" often means the model said so. LitGrok gives each session a routine to follow: a written plan before the first edit, checks against that plan, and a handoff file the next session can read. It ships 38 skills, 11 agents, one project rule, and eleven hook registrations. Grok Build still runs the session and chooses the model.
17
+ Long Grok Build sessions get compacted, a new session starts without what the last one knew, and "done" often just means the model said so. LitGrok gives each session a routine to follow: a written plan before the first edit, checks against that plan, and a handoff file the next session can read. It ships 38 skills, 11 agents, one project rule, and eleven hook registrations. Grok Build still runs the session and chooses the model. It suits work that runs longer than one sitting, such as a feature spread over a few days or a report someone else will read.
18
18
 
19
19
  ## Install in 30 seconds
20
20
 
@@ -24,7 +24,7 @@ You need Node.js and Grok Build. In an interactive terminal, from the project yo
24
24
  npm exec --yes --package @litfamily/litgrok@latest -- litgrok install
25
25
  ```
26
26
 
27
- The files land in `<project>/.grok/`. Add `--user` to install under `~/.grok/` instead, or use `--package @litfamily/litgrok@1.0.22` to pin this release. Add `--dry-run` to see every destination before anything is written.
27
+ The files land in `<project>/.grok/`. Add `--user` to install under `~/.grok/` instead, or use `--package @litfamily/litgrok@1.0.24` to pin this release. Add `--dry-run` to see every destination before anything is written.
28
28
 
29
29
  Trying a local package instead? Put its real absolute path in a variable, then run these two lines separately:
30
30
 
@@ -72,7 +72,7 @@ Beyond code, `lit-pptx` makes slide decks and `lit-docx` makes reports and Word
72
72
 
73
73
  ## What changes after install
74
74
 
75
- The installer copies the skills, agents, project rule, and hooks into `.grok/`. The eleven hook registrations in [`hooks/hooks.json`](https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.22/hooks/hooks.json) run only in a trusted Git root, and they log the order of events in `.grok/litgrok/session-ledger/`. Everything runs inside your Grok session, and nothing runs in the background; when the session ends, the work waits for the next one.
75
+ The installer copies the skills, agents, project rule, and hooks into `.grok/`. The eleven hook registrations in [`hooks/hooks.json`](https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.24/hooks/hooks.json) run only in a trusted Git root, and they log the order of events in `.grok/litgrok/session-ledger/`. Everything runs inside your Grok session, and nothing runs in the background; when the session ends, the work waits for the next one.
76
76
 
77
77
  If you'd like a status row showing the active LitGrok skill, the model and how much context is used, add it with `install --user --status-line`. It writes `[ui.status_line]` into `~/.grok/config.toml` and backs up an existing file first. Once a prompt starts a workflow skill, the row also shows the workflow stage, the tool running now and the last test result. Add `--notify` for Grok's terminal notification when a turn finishes.
78
78
 
@@ -99,9 +99,11 @@ The installer never runs `git init`, trusts hooks, signs in or picks a model for
99
99
 
100
100
  ## If something does not work
101
101
 
102
- - **No hooks:** Grok runs hooks only from a trusted Git project root. For a new project, run `git init` yourself, then trust it; for an existing one, open Grok Build from its real root.
103
- - **Nothing was written:** the output says `no files written` and names the reason (`DRY RUN`, `NO COLOR` or `NON-INTERACTIVE`). Fix that case from the list above and run the install again.
104
- - **No status row:** Grok reads the row from user or administrator configuration, not from a project or plugin, so add it with `install --user --status-line`.
102
+ If the hooks don't show up, check where Grok Build was opened. Grok runs hooks only from a trusted Git project root, so in a new project run `git init` yourself and then trust it, and in an existing one open Grok Build from its real root.
103
+
104
+ If the installer wrote nothing, the output says `no files written` and names the reason: `DRY RUN`, `NO COLOR` or `NON-INTERACTIVE`. Fix that case from the list above and run the install again.
105
+
106
+ If the status row is missing, add it with `install --user --status-line`. Grok reads the row only from user or administrator configuration, which is why a project install can't turn it on.
105
107
 
106
108
  ---
107
109
 
@@ -109,6 +111,6 @@ The installer never runs `git init`, trusts hooks, signs in or picks a model for
109
111
 
110
112
  If LitGrok saves you some re-explaining, a [star on GitHub](https://github.com/wjgoarxiv/litgrok) helps other Grok Build users find it.
111
113
 
112
- [Reference](https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.22/docs/reference.md) · [Changelog](https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.22/CHANGELOG.md) · [Privacy](https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.22/docs/privacy.md) · [MIT license](https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.22/LICENSE)
114
+ [Reference](https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.24/docs/reference.md) · [Changelog](https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.24/CHANGELOG.md) · [Privacy](https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.24/docs/privacy.md) · [MIT license](https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.24/LICENSE)
113
115
 
114
116
  The cover is brand motion made with the LitFamily motion skill: animated artwork, with no Grok session recorded in it.
package/README_ko-KR.md CHANGED
@@ -1,20 +1,20 @@
1
- <p align="center"><picture><source media="(prefers-reduced-motion: reduce)" srcset="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.22/docs/assets/cover-motion-still.webp" /><source media="(prefers-reduced-motion: no-preference)" srcset="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.22/docs/assets/cover-motion.webp" /><img src="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.22/docs/assets/cover-motion.webp" width="100%" alt="LitFamily 모션 커버: 다섯 로봇 패널이 차례로 켜지고, LitGrok 로봇의 눈과 테두리가 빛난 뒤 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/litgrok@1.0.24/docs/assets/cover-motion-still.webp" /><source media="(prefers-reduced-motion: no-preference)" srcset="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.24/docs/assets/cover-motion.webp" /><img src="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.24/docs/assets/cover-motion.webp" width="100%" alt="LitFamily 모션 커버: 다섯 로봇 패널이 차례로 켜지고, LitGrok 로봇의 눈과 테두리가 빛난 뒤 LITFAMILY와 KEEP THE WORK LIT. 문구가 밝아지는 영상" /></picture></p>
2
2
 
3
3
  <h1 align="center">LitGrok</h1>
4
4
  <p align="center"><strong>Keep the work lit.</strong></p>
5
5
 
6
- xAI Grok Build에 스킬, 프로젝트 규칙, 훅을 더해 세션마다 계획하고, 만들고, 확인하고, 다음 세션이 이어받을 인수인계 문서를 남기게 합니다.
6
+ LitGrok은 xAI Grok Build가 코드를 고치기 전에 계획부터 세우고, 만든 결과를 확인하고, 인수인계 문서를 남기게 합니다. 그러면 내일 세션이 오늘 멈춘 자리에서 이어집니다. npm 명령 한 줄이면 스킬과 프로젝트 규칙, 훅이 프로젝트에 들어갑니다.
7
7
 
8
- **[GitHub에서 README 전체 보기](https://github.com/wjgoarxiv/litgrok/blob/main/README_ko-KR.md)** · [안내서](https://github.com/wjgoarxiv/litgrok/blob/main/docs/guide_ko-KR.md) · [전체 스킬](https://github.com/wjgoarxiv/litgrok/blob/main/docs/skills_ko-KR.md) · [English](https://github.com/wjgoarxiv/litgrok#readme) · [설치](#30초-설치) · [상세 안내](https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.22/docs/reference_ko-KR.md)
8
+ **[GitHub에서 README 전체 보기](https://github.com/wjgoarxiv/litgrok/blob/main/README_ko-KR.md)** · [안내서](https://github.com/wjgoarxiv/litgrok/blob/main/docs/guide_ko-KR.md) · [전체 스킬](https://github.com/wjgoarxiv/litgrok/blob/main/docs/skills_ko-KR.md) · [English](https://github.com/wjgoarxiv/litgrok#readme) · [설치](#30초-설치) · [상세 안내](https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.24/docs/reference_ko-KR.md)
9
9
 
10
10
  <p align="center">
11
- <a href="#30초-설치"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.22/docs/assets/readme/badge-version.svg" alt="1.0.22" /></a>
12
- <a href="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.22/LICENSE"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.22/docs/assets/readme/badge-license.svg" alt="MIT 라이선스" /></a>
11
+ <a href="#30초-설치"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.24/docs/assets/readme/badge-version.svg" alt="1.0.24" /></a>
12
+ <a href="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.24/LICENSE"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.24/docs/assets/readme/badge-license.svg" alt="MIT 라이선스" /></a>
13
13
  </p>
14
14
 
15
- <p align="center"><a href="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.22/docs/reference_ko-KR.md"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.22/docs/assets/readme/lucide-book-open.svg" width="16" alt="" /> 상세 안내</a> &nbsp; <a href="#30초-설치">설치</a> &nbsp; <a href="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.22/docs/assets/cover-motion.webp"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.22/docs/assets/readme/lucide-play.svg" width="16" alt="" /> 커버 모션</a> &nbsp; <a href="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.22/LICENSE"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.22/docs/assets/readme/lucide-shield-check.svg" width="16" alt="" /> MIT</a></p>
15
+ <p align="center"><a href="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.24/docs/reference_ko-KR.md"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.24/docs/assets/readme/lucide-book-open.svg" width="16" alt="" /> 상세 안내</a> &nbsp; <a href="#30초-설치">설치</a> &nbsp; <a href="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.24/docs/assets/cover-motion.webp"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.24/docs/assets/readme/lucide-play.svg" width="16" alt="" /> 커버 모션</a> &nbsp; <a href="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.24/LICENSE"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.24/docs/assets/readme/lucide-shield-check.svg" width="16" alt="" /> MIT</a></p>
16
16
 
17
- Grok Build 세션이 길어지면 대화가 압축되고, 새 세션은 앞 세션이 알던 내용을 모른 채 시작하며, "다 됐다"는 말도 모델이 그렇게 말했다는 뜻일 때가 많습니다. LitGrok은 세션마다 따라갈 순서를 줍니다. 첫 수정 전에 계획을 글로 남기고, 그 계획에 비추어 확인하고, 다음 세션이 읽을 인수인계 문서를 남깁니다. 스킬 38개, 에이전트 11개, 프로젝트 규칙 하나, 훅 등록 열한 개가 들어 있고, 세션 실행과 모델 선택은 계속 Grok Build가 맡습니다.
17
+ Grok Build 세션이 길어지면 대화가 압축되고, 새 세션은 앞 세션이 알던 내용을 모른 채 시작하며, "다 됐다"는 말도 모델이 그렇게 말했다는 뜻일 때가 많습니다. LitGrok은 세션마다 따라갈 순서를 줍니다. 첫 수정 전에 계획을 글로 남기고, 그 계획에 비추어 확인하고, 다음 세션이 읽을 인수인계 문서를 남깁니다. 스킬 38개, 에이전트 11개, 프로젝트 규칙 하나, 훅 등록 열한 개가 들어 있고, 세션 실행과 모델 선택은 계속 Grok Build가 맡습니다. 며칠에 걸쳐 만드는 기능이나 다른 사람이 읽을 보고서처럼, 한 번 앉은 자리에서 끝나지 않는 일에 잘 맞습니다.
18
18
 
19
19
  ## 30초 설치
20
20
 
@@ -24,7 +24,7 @@ Node.js와 Grok Build가 있으면 됩니다. 써 보고 싶은 프로젝트에
24
24
  npm exec --yes --package @litfamily/litgrok@latest -- litgrok install
25
25
  ```
26
26
 
27
- 파일은 `<project>/.grok/`에 들어갑니다. `~/.grok/`에 설치하려면 `--user`를, 이 릴리스로 고정하려면 `--package @litfamily/litgrok@1.0.22`을 쓰세요. `--dry-run`을 붙이면 파일을 쓰기 전에 들어갈 경로를 모두 보여 줍니다.
27
+ 파일은 `<project>/.grok/`에 들어갑니다. `~/.grok/`에 설치하려면 `--user`를, 이 릴리스로 고정하려면 `--package @litfamily/litgrok@1.0.24`을 쓰세요. `--dry-run`을 붙이면 파일을 쓰기 전에 들어갈 경로를 모두 보여 줍니다.
28
28
 
29
29
  로컬 패키지로 써 보려면 실제 절대 경로를 변수에 넣고 두 줄을 따로 실행하세요.
30
30
 
@@ -35,7 +35,7 @@ npm exec --yes --package "$LITGROK_PACK" -- litgrok install
35
35
 
36
36
  ## 처음 입력해 볼 것
37
37
 
38
- 프로젝트의 Git 루트에서 Grok Build를 열거나 다시 시작합니다. Grok은 신뢰한 프로젝트 훅만 실행하니, `/hooks-trust`에서 훅을 살펴보고 신뢰해 주세요. Grok Build 1.0.13에서는 `grok --trust inspect --json`으로도 같은 일을 할 수 있었습니다. Git이 없는 일반 폴더에서는 스킬과 규칙은 불러와도 훅은 잡히지 않을 수 있습니다. 그다음 이렇게 보내 보세요.
38
+ 프로젝트의 Git 루트에서 Grok Build를 열거나 다시 시작합니다. Grok은 신뢰한 프로젝트 훅만 실행하니, `/hooks-trust`에서 훅을 살펴보고 신뢰해 주세요. Grok Build 1.0.13에서는 `grok --trust inspect --json`으로도 같은 일을 할 수 있었습니다. Git이 없는 일반 폴더에서는 스킬과 규칙은 불러와도 훅은 꺼진 채로 남습니다. 그다음 이렇게 보내 보세요.
39
39
 
40
40
  ```text
41
41
  /litwork 외부 의존성 없이 index.html 하나로 할 일 목록을 만들어줘. 추가·완료·삭제 동작을 구현하고, 확인한 내용과 다음 할 일을 남겨줘. 브라우저를 자동으로 열지 말고 내가 확인할 순서를 알려줘.
@@ -72,7 +72,7 @@ npm exec --yes --package "$LITGROK_PACK" -- litgrok install
72
72
 
73
73
  ## 설치하면 달라지는 것
74
74
 
75
- 설치 프로그램이 스킬, 에이전트, 프로젝트 규칙, 훅을 `.grok/`에 복사합니다. [`hooks/hooks.json`](https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.22/hooks/hooks.json)에 있는 훅 등록 열한 개는 신뢰한 Git 루트에서만 동작하고, `.grok/litgrok/session-ledger/`에 이벤트 순서를 남깁니다. 모든 작업은 Grok 세션 안에서 돌고 백그라운드 작업은 없습니다. 세션이 끝나면 다음 세션이 이어받을 때까지 그 자리에 멈춰 있습니다.
75
+ 설치 프로그램이 스킬, 에이전트, 프로젝트 규칙, 훅을 `.grok/`에 복사합니다. [`hooks/hooks.json`](https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.24/hooks/hooks.json)에 있는 훅 등록 열한 개는 신뢰한 Git 루트에서만 동작하고, `.grok/litgrok/session-ledger/`에 이벤트 순서를 남깁니다. 모든 작업은 Grok 세션 안에서 돌고 백그라운드 작업은 없습니다. 세션이 끝나면 다음 세션이 이어받을 때까지 그 자리에 멈춰 있습니다.
76
76
 
77
77
  지금 어떤 LitGrok 스킬이 동작 중인지, 어떤 모델을 쓰는지, 컨텍스트를 얼마나 썼는지 보고 싶으면 `install --user --status-line`으로 상태 행을 켜세요. `~/.grok/config.toml`에 `[ui.status_line]`을 넣고, 파일이 이미 있으면 먼저 백업합니다. 프롬프트가 작업 흐름 스킬을 시작하면 지금 단계, 돌고 있는 도구, 마지막 테스트 결과도 함께 보입니다. 턴이 끝날 때 Grok의 터미널 알림을 받으려면 `--notify`를 붙이세요.
78
78
 
@@ -99,9 +99,11 @@ npm exec --yes --package @litfamily/litgrok@latest -- litgrok uninstall --user
99
99
 
100
100
  ## 잘 안 될 때
101
101
 
102
- - **훅이 보이지 않을 때:** Grok은 신뢰한 Git 프로젝트 루트에서만 훅을 실행합니다. 새 프로젝트라면 신뢰하기 전에 직접 `git init`을 실행하고, 기존 저장소라면 실제 루트에서 Grok Build를 여세요.
103
- - **파일이 쓰이지 않았을 때:** 출력에 `no files written`과 함께 `DRY RUN`, `NO COLOR`, `NON-INTERACTIVE` 가운데 이유가 나옵니다. 위 목록에서 해당하는 경우를 고치고 다시 설치하세요.
104
- - **상태 행이 보이지 않을 때:** Grok은 상태 행을 프로젝트나 플러그인 설정이 아닌 사용자나 관리자 설정에서 읽으므로 `install --user --status-line`으로 켜야 합니다.
102
+ 훅이 보이지 않으면 Grok Build를 어디서 열었는지부터 보세요. Grok은 신뢰한 Git 프로젝트 루트에서만 훅을 실행합니다. 새 프로젝트라면 직접 `git init`을 실행한 뒤 신뢰하고, 기존 저장소라면 실제 루트에서 Grok Build를 여세요.
103
+
104
+ 아무 파일도 쓰이지 않았다면 출력에 `no files written`과 함께 `DRY RUN`, `NO COLOR`, `NON-INTERACTIVE` 가운데 하나가 이유로 나옵니다. 위 목록에서 해당하는 경우를 고치고 다시 설치하세요.
105
+
106
+ 상태 행이 안 보이면 `install --user --status-line`으로 켜세요. Grok은 상태 행을 사용자나 관리자 설정에서만 읽기 때문에 프로젝트 설치로는 켤 수 없습니다.
105
107
 
106
108
  ---
107
109
 
@@ -109,6 +111,6 @@ npm exec --yes --package @litfamily/litgrok@latest -- litgrok uninstall --user
109
111
 
110
112
  LitGrok 덕분에 같은 설명을 덜 반복하게 됐다면, [GitHub 스타](https://github.com/wjgoarxiv/litgrok) 하나가 다른 Grok Build 사용자에게 이 프로젝트를 알리는 데 도움이 됩니다.
111
113
 
112
- [상세 안내](https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.22/docs/reference_ko-KR.md) · [변경 이력](https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.22/CHANGELOG.md) · [개인정보](https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.22/docs/privacy.md) · [MIT 라이선스](https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.22/LICENSE)
114
+ [상세 안내](https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.24/docs/reference_ko-KR.md) · [변경 이력](https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.24/CHANGELOG.md) · [개인정보](https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.24/docs/privacy.md) · [MIT 라이선스](https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.24/LICENSE)
113
115
 
114
116
  커버의 모션은 LitFamily 모션 스킬로 만든 브랜드 연출입니다. 그려서 움직인 그림이라 실제 Grok 세션을 녹화한 장면은 들어 있지 않습니다.
@@ -1 +1 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" width="146" height="20" role="img" aria-label="local: 1.0.22"><title>local: 1.0.22</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="290" transform="scale(.1)">local</text><text x="965" y="140" textLength="870" transform="scale(.1)" fill="#080D14">1.0.22</text></g></svg>
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="146" height="20" role="img" aria-label="local: 1.0.24"><title>local: 1.0.24</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="290" transform="scale(.1)">local</text><text x="965" y="140" textLength="870" transform="scale(.1)" fill="#080D14">1.0.24</text></g></svg>
package/docs/reference.md CHANGED
@@ -77,7 +77,7 @@ npm exec --yes --package @litfamily/litgrok@latest -- litgrok install
77
77
  Pin a version when you need a reproducible install:
78
78
 
79
79
  ```bash
80
- npm exec --yes --package @litfamily/litgrok@1.0.22 -- litgrok install
80
+ npm exec --yes --package @litfamily/litgrok@1.0.24 -- litgrok install
81
81
  ```
82
82
 
83
83
  Preview without writing files:
@@ -77,7 +77,7 @@ npm exec --yes --package @litfamily/litgrok@latest -- litgrok install
77
77
  재현 가능한 설치는 버전을 고정합니다.
78
78
 
79
79
  ```bash
80
- npm exec --yes --package @litfamily/litgrok@1.0.22 -- litgrok install
80
+ npm exec --yes --package @litfamily/litgrok@1.0.24 -- litgrok install
81
81
  ```
82
82
 
83
83
  파일을 쓰지 않고 미리보기:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@litfamily/litgrok",
3
- "version": "1.0.22",
3
+ "version": "1.0.24",
4
4
  "description": "Installer for LitGrok: skills, a project rule and hooks that help xAI Grok Build sessions plan, build, check and hand off their work.",
5
5
  "keywords": [
6
6
  "grok",
package/plugin.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "litgrok",
3
- "version": "1.0.22",
3
+ "version": "1.0.24",
4
4
  "description": "Grok Build skills, project rules, and bounded lifecycle hooks for evidence-first lit work.",
5
5
  "author": {
6
6
  "name": "LitGrok contributors"