@litfamily/litgrok 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.
Files changed (123) hide show
  1. package/.grok/hooks/auto-handoff.mjs +1 -1
  2. package/.grok/hooks/deliverable-hedge-guard.mjs +100 -15
  3. package/.grok/hooks/lit-status-line.mjs +414 -269
  4. package/.grok/hooks/plan-progress.mjs +11 -2
  5. package/.grok/hooks/record-passive-event.mjs +20 -9
  6. package/.grok/hooks/workflow-stage.mjs +48 -13
  7. package/.grok/rules/00-litgrok.md +21 -12
  8. package/.grok/skills/autoconference/SKILL.md +3 -1
  9. package/.grok/skills/autoresearch/SKILL.md +3 -1
  10. package/.grok/skills/browser-drive/SKILL.md +3 -1
  11. package/.grok/skills/browser-drive/scripts/capability-probe.mjs +2 -2
  12. package/.grok/skills/comment-checker/SKILL.md +3 -1
  13. package/.grok/skills/debugging/SKILL.md +3 -1
  14. package/.grok/skills/deep-interview/SKILL.md +4 -1
  15. package/.grok/skills/frontend-ui-ux/scripts/probe.mjs +3 -2
  16. package/.grok/skills/lit-brainstorm/SKILL.md +162 -0
  17. package/.grok/skills/lit-brainstorm/references/converge-and-score.md +85 -0
  18. package/.grok/skills/lit-brainstorm/references/handoff-and-notes.md +66 -0
  19. package/.grok/skills/lit-brainstorm/references/idea-lane-packet.md +58 -0
  20. package/.grok/skills/lit-brainstorm/references/idea-lenses.md +60 -0
  21. package/.grok/skills/lit-diagram-drawer/scripts/diagram-metrics.mjs +1 -1
  22. package/.grok/skills/lit-diagram-drawer/scripts/export.mjs +6 -6
  23. package/.grok/skills/lit-diagram-drawer/scripts/renderer-status.mjs +3 -3
  24. package/.grok/skills/lit-docx/SKILL.md +3 -1
  25. package/.grok/skills/lit-docx/scripts/convert_md_to_pdf.py +7 -4
  26. package/.grok/skills/lit-docx/scripts/docx_gate.py +5 -2
  27. package/.grok/skills/lit-docx/scripts/pdf_fonts.py +63 -0
  28. package/.grok/skills/lit-docx/scripts/run.mjs +3 -5
  29. package/.grok/skills/lit-docx/templates/latex/acs/template.tex +5 -0
  30. package/.grok/skills/lit-docx/templates/latex/elsevier/template.tex +5 -0
  31. package/.grok/skills/lit-docx/templates/latex/ieee/template.tex +5 -0
  32. package/.grok/skills/lit-docx/templates/latex/korean-generic/template.tex +5 -0
  33. package/.grok/skills/lit-docx/templates/latex/nature/template.tex +5 -0
  34. package/.grok/skills/lit-handoff/SKILL.md +3 -1
  35. package/.grok/skills/lit-humanizer/SKILL.md +4 -0
  36. package/.grok/skills/lit-humanizer/assets/always-on-rule.md +3 -1
  37. package/.grok/skills/lit-humanizer/fixtures/rule-cases.json +4 -2
  38. package/.grok/skills/lit-humanizer/rules.json +24 -0
  39. package/.grok/skills/lit-humanizer/scripts/core.mjs +56 -1
  40. package/.grok/skills/lit-humanizer/scripts/extract.mjs +6 -3
  41. package/.grok/skills/lit-humanizer/scripts/test-fixtures.mjs +3 -1
  42. package/.grok/skills/lit-plan/SKILL.md +2 -0
  43. package/.grok/skills/lit-pptx/SKILL.md +3 -1
  44. package/.grok/skills/lit-pptx/scripts/deps.cjs +18 -8
  45. package/.grok/skills/lit-pptx/scripts/proc.cjs +192 -0
  46. package/.grok/skills/lit-pptx/scripts/run.mjs +3 -5
  47. package/.grok/skills/lit-recap/SKILL.md +3 -1
  48. package/.grok/skills/lit-typographic-motion/SKILL.md +9 -6
  49. package/.grok/skills/lit-typographic-motion/THIRD_PARTY_NOTICES +3 -0
  50. package/.grok/skills/lit-typographic-motion/fonts/JetBrainsMono-OFL.txt +93 -0
  51. package/.grok/skills/lit-typographic-motion/fonts/JetBrainsMono-wght-latin.woff2 +0 -0
  52. package/.grok/skills/lit-typographic-motion/fonts/pins.json +15 -0
  53. package/.grok/skills/lit-typographic-motion/recipes/negatives/card-scenes/expected.json +16 -0
  54. package/.grok/skills/lit-typographic-motion/recipes/negatives/card-scenes/index.html +36 -0
  55. package/.grok/skills/lit-typographic-motion/recipes/negatives/reset-sweep/expected.json +16 -0
  56. package/.grok/skills/lit-typographic-motion/recipes/negatives/reset-sweep/index.html +24 -0
  57. package/.grok/skills/lit-typographic-motion/recipes/negatives/still-glow/expected.json +17 -0
  58. package/.grok/skills/lit-typographic-motion/recipes/negatives/still-glow/index.html +25 -0
  59. package/.grok/skills/lit-typographic-motion/recipes/negatives/title-slam/expected.json +17 -0
  60. package/.grok/skills/lit-typographic-motion/recipes/negatives/title-slam/index.html +37 -0
  61. package/.grok/skills/lit-typographic-motion/recipes/orbit-loop/expected.json +67 -0
  62. package/.grok/skills/lit-typographic-motion/recipes/orbit-loop/index.html +119 -0
  63. package/.grok/skills/lit-typographic-motion/recipes/painted-light/expected.json +95 -0
  64. package/.grok/skills/lit-typographic-motion/recipes/painted-light/index.html +192 -0
  65. package/.grok/skills/lit-typographic-motion/recipes/weight-tide/expected.json +67 -0
  66. package/.grok/skills/lit-typographic-motion/recipes/weight-tide/index.html +85 -0
  67. package/.grok/skills/lit-typographic-motion/references/cover.md +380 -0
  68. package/.grok/skills/lit-typographic-motion/references/craft-loop.md +4 -0
  69. package/.grok/skills/lit-typographic-motion/references/runtime.md +14 -3
  70. package/.grok/skills/lit-typographic-motion/references/stage.md +2 -1
  71. package/.grok/skills/lit-typographic-motion/references/treatment.md +5 -0
  72. package/.grok/skills/lit-typographic-motion/scripts/lib/cache.mjs +1 -1
  73. package/.grok/skills/lit-typographic-motion/scripts/lib/chrome.mjs +14 -4
  74. package/.grok/skills/lit-typographic-motion/scripts/lib/constants.mjs +39 -0
  75. package/.grok/skills/lit-typographic-motion/scripts/lib/cover-assets.mjs +88 -0
  76. package/.grok/skills/lit-typographic-motion/scripts/lib/cover-capture.mjs +144 -0
  77. package/.grok/skills/lit-typographic-motion/scripts/lib/cover-gate.mjs +312 -0
  78. package/.grok/skills/lit-typographic-motion/scripts/lib/cover-image.mjs +111 -0
  79. package/.grok/skills/lit-typographic-motion/scripts/lib/cover-pool.mjs +62 -0
  80. package/.grok/skills/lit-typographic-motion/scripts/lib/cover-sheet.mjs +73 -0
  81. package/.grok/skills/lit-typographic-motion/scripts/lib/cover-treatment.mjs +104 -0
  82. package/.grok/skills/lit-typographic-motion/scripts/lib/cover-webp.mjs +134 -0
  83. package/.grok/skills/lit-typographic-motion/scripts/lib/cover.mjs +347 -0
  84. package/.grok/skills/lit-typographic-motion/scripts/lib/encode.mjs +1 -1
  85. package/.grok/skills/lit-typographic-motion/scripts/lib/gate.mjs +2 -2
  86. package/.grok/skills/lit-typographic-motion/scripts/lib/look.mjs +99 -7
  87. package/.grok/skills/lit-typographic-motion/scripts/lib/render.mjs +8 -11
  88. package/.grok/skills/lit-typographic-motion/scripts/lib/sound.mjs +1 -1
  89. package/.grok/skills/lit-typographic-motion/scripts/lib/stage.mjs +3 -1
  90. package/.grok/skills/lit-typographic-motion/scripts/lib/treatment.mjs +1 -0
  91. package/.grok/skills/lit-typographic-motion/scripts/motion.mjs +45 -3
  92. package/.grok/skills/lit-typographic-motion/scripts/prewarm.mjs +35 -22
  93. package/.grok/skills/lit-typographic-motion/scripts/stage/cover-init.js +44 -0
  94. package/.grok/skills/lit-typographic-motion/scripts/stage/loop-kit.js +96 -0
  95. package/.grok/skills/litgoal/SKILL.md +14 -2
  96. package/.grok/skills/litgrok/SKILL.md +30 -7
  97. package/.grok/skills/litresearch/SKILL.md +3 -1
  98. package/.grok/skills/litwork/SKILL.md +12 -1
  99. package/.grok/skills/lsp/SKILL.md +3 -1
  100. package/.grok/skills/lsp-setup/SKILL.md +3 -1
  101. package/.grok/skills/lsp-setup/scripts/lsp-server-table.mjs +3 -3
  102. package/.grok/skills/refactor/SKILL.md +3 -1
  103. package/.grok/skills/review-work/SKILL.md +20 -26
  104. package/.grok/skills/rules/SKILL.md +3 -1
  105. package/.grok/skills/start-work/SKILL.md +1 -1
  106. package/.grok/skills/structural-search/SKILL.md +3 -1
  107. package/.grok/skills/wikify/SKILL.md +2 -0
  108. package/.grok-plugin/plugin.json +2 -1
  109. package/CHANGELOG.md +19 -0
  110. package/README.md +12 -11
  111. package/README_ko-KR.md +12 -11
  112. package/bin/litgrok.mjs +7 -2
  113. package/bin/status-line-config.mjs +26 -14
  114. package/docs/assets/cover-motion-still.webp +0 -0
  115. package/docs/assets/cover-motion.webp +0 -0
  116. package/docs/assets/cover.webp +0 -0
  117. package/docs/assets/readme/README.md +5 -5
  118. package/docs/assets/readme/badge-version.svg +1 -1
  119. package/docs/assets/skills/lit-brainstorm.webp +0 -0
  120. package/docs/reference.md +18 -8
  121. package/docs/reference_ko-KR.md +19 -8
  122. package/package.json +3 -2
  123. package/plugin.json +1 -1
@@ -245,7 +245,7 @@ export async function runAutoHandoff(args, { env = process.env, stdout = process
245
245
 
246
246
  const located = projectRoot(cwd);
247
247
  const { root } = located;
248
- const notes = rootNotes(located, env.HOME || null);
248
+ const notes = rootNotes(located, env.HOME || env.USERPROFILE || null);
249
249
  const file = `Project file: ${configPath(root)}`;
250
250
  const resolved = resolveAutoHandoff({ env, dirs: [root] });
251
251
  if (action === 'status') {
@@ -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
  }