@litfamily/litgrok 1.0.28 → 1.0.30

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/bin/litgrok.mjs CHANGED
@@ -6,6 +6,7 @@ import { homedir } from 'node:os';
6
6
  import { basename, dirname, join, relative, resolve, sep } from 'node:path';
7
7
  import { fileURLToPath } from 'node:url';
8
8
  import { renderMark, terminalMode } from '../.grok/hooks/lit-mark.mjs';
9
+ import { applyStoredGround, backupIfSectionAbsent, decideGround, groundReport, ruleSourceForInstall, runGround } from './ground-rule.mjs';
9
10
  import { installNotifications, installStatusLine, removeNotifications, removeStatusLine } from './status-line-config.mjs';
10
11
 
11
12
  const PACKAGE_ROOT = dirname(dirname(fileURLToPath(import.meta.url)));
@@ -208,7 +209,7 @@ const PRE_MANIFEST_LEGACY_PAYLOAD_HASHES = Object.freeze({
208
209
  'skills/text-naturalization/SKILL.md': 'aa1ff8f68ab0bf71cb47818b536c4a2934da65adb413ee43ec5839b540b2df8e',
209
210
  'skills/lit-korean/SKILL.md': 'dc2e4b0ea2ff6acca904f05767a62448387571e4183a615d5e53b230704ee828',
210
211
  });
211
- const USAGE = 'Usage: litgrok [install|uninstall] [--user|--project] [--status-line] [--notify] [--dry-run] [--no-color] [--yes]\n litgrok auto-handoff on [percent]|off|status\n litgrok doctor [--no-color]\n litgrok motion-runtime install|status\nTypographic-motion engine adapted from mexicat/pdoom-video (MIT, Giacomo Magnanini), commit ca251e3.\n';
212
+ const USAGE = 'Usage: litgrok [install|uninstall] [--user|--project] [--status-line] [--notify] [--dry-run] [--no-color] [--yes] [--no-ground]\n litgrok ground on|off|status\n litgrok ground project [--dir <path>] [--remove] [--dry-run]\n litgrok auto-handoff on [percent]|off|status\n litgrok doctor [--no-color]\n litgrok motion-runtime install|status\nTypographic-motion engine adapted from mexicat/pdoom-video (MIT, Giacomo Magnanini), commit ca251e3.\n';
212
213
  const BRAILLE_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
213
214
  const SPINNER_INTERVAL_MS = 80;
214
215
  const CLEAR_LINE = '\r\u001b[2K';
@@ -217,7 +218,7 @@ function parseInstall(args) {
217
218
  const remaining = [...args];
218
219
  let command = 'install';
219
220
  if (['install', 'uninstall'].includes(remaining[0])) command = remaining.shift();
220
- if (remaining.some((argument) => !['--user', '--project', '--status-line', '--notify', '--dry-run', '--no-color', '--yes'].includes(argument))) return null;
221
+ if (remaining.some((argument) => !['--user', '--project', '--status-line', '--notify', '--dry-run', '--no-color', '--yes', '--no-ground'].includes(argument))) return null;
221
222
  if (remaining.includes('--user') && remaining.includes('--project')) return null;
222
223
  // Both opt-ins change ~/.grok/config.toml, which only a user install may touch.
223
224
  if ((remaining.includes('--status-line') || remaining.includes('--notify')) && (command !== 'install' || !remaining.includes('--user'))) return null;
@@ -226,6 +227,7 @@ function parseInstall(args) {
226
227
  dryRun: remaining.includes('--dry-run'),
227
228
  noColor: remaining.includes('--no-color'),
228
229
  yes: remaining.includes('--yes'),
230
+ noGround: remaining.includes('--no-ground'),
229
231
  statusLine: remaining.includes('--status-line'),
230
232
  notify: remaining.includes('--notify'),
231
233
  scope: remaining.includes('--user') ? 'user' : 'project',
@@ -833,7 +835,7 @@ async function runDoctor(args, { env, stdout, stderr, fetchFn }) {
833
835
  return 1;
834
836
  }
835
837
  const current = packageVersion();
836
- stdout.write(`litgrok ${current}\n`);
838
+ stdout.write(`litgrok ${current}\n${groundReport(env)}\n`);
837
839
  if (UPDATE_OPT_OUTS.some((name) => Object.hasOwn(env, name))) return 0;
838
840
  let latest;
839
841
  try {
@@ -855,6 +857,7 @@ export async function run(args, context = {}) {
855
857
  const { runAutoHandoff } = await import('../.grok/hooks/auto-handoff.mjs');
856
858
  return runAutoHandoff(args.slice(1), { env: context.env ?? process.env, stdout: context.stdout ?? process.stdout, stderr: context.stderr ?? process.stderr, cwd: context.cwd ?? process.cwd() });
857
859
  }
860
+ if (args[0] === 'ground') return runGround(args.slice(1), context);
858
861
  if (args[0] === 'doctor') {
859
862
  return runDoctor(args.slice(1), {
860
863
  env: context.env ?? process.env,
@@ -906,7 +909,7 @@ export async function run(args, context = {}) {
906
909
  if (projectHooksIncluded(files)) stdout.write('Project hooks require a trusted Git project root: use /hooks-trust or, on Grok Build 1.0.13, grok --trust inspect --json; the installer never runs git init or changes trust.\n');
907
910
  }
908
911
 
909
- const files = discoveredPayload ? filesFromPayload(discoveredPayload) : null;
912
+ let files = discoveredPayload ? filesFromPayload(discoveredPayload) : null;
910
913
  const payloadElapsed = payloadError ? null : elapsedSince(payloadStarted);
911
914
  const colors = printInstallFrame(stdout, options, env, files?.length, displayRoot);
912
915
 
@@ -930,6 +933,33 @@ export async function run(args, context = {}) {
930
933
  return 0;
931
934
  }
932
935
 
936
+ let includeGround = true;
937
+ let groundAfter = null;
938
+ let cleanupGround = () => {};
939
+ if (options.command === 'install') {
940
+ try {
941
+ const decision = await decideGround({
942
+ scope: options.scope,
943
+ yes: options.yes,
944
+ noGround: options.noGround,
945
+ env,
946
+ stdin,
947
+ stdout,
948
+ home,
949
+ });
950
+ includeGround = decision.include;
951
+ groundAfter = decision.afterWrite;
952
+ if (!decision.include) {
953
+ const prepared = ruleSourceForInstall(files);
954
+ files = prepared.files;
955
+ cleanupGround = prepared.cleanup;
956
+ }
957
+ } catch (error) {
958
+ stderr.write(`litgrok: ${error instanceof Error ? error.message : String(error)}\n`);
959
+ return 1;
960
+ }
961
+ }
962
+
933
963
  const progress = new InstallProgress(stdout, colors);
934
964
  progress.open();
935
965
  let step = 'PAYLOAD';
@@ -1046,6 +1076,7 @@ export async function run(args, context = {}) {
1046
1076
  throw error;
1047
1077
  }
1048
1078
  const statusDetail = configDetail(statusLineResult, notifyResult);
1079
+ if (groundAfter) applyStoredGround(home, groundAfter);
1049
1080
  skipStep(progress, 'WRITE', `SKIPPED · 0 written · ${states.counts.current} already-current${statusDetail}`);
1050
1081
  const receipt = receiptCard(options, displayRoot, colors, { status: 'Already current', written: 0, current: states.counts.current });
1051
1082
  progress.close();
@@ -1059,8 +1090,11 @@ export async function run(args, context = {}) {
1059
1090
  try {
1060
1091
  for (let index = 0; index < files.length; index += 1) {
1061
1092
  if (states.states[index] === 'current') continue;
1062
- const { source, target } = files[index];
1093
+ const { source, target, relativePath } = files[index];
1063
1094
  mkdirSync(dirname(target), { recursive: true });
1095
+ if (includeGround && normalizedRelativePath(relativePath) === 'rules/00-litgrok.md' && states.states[index] !== 'missing') {
1096
+ backupIfSectionAbsent(target);
1097
+ }
1064
1098
  if (states.states[index] === 'owned') replaceOwnedFile(source, target);
1065
1099
  else copyFileSync(source, target, constants.COPYFILE_EXCL);
1066
1100
  installed += 1;
@@ -1072,6 +1106,7 @@ export async function run(args, context = {}) {
1072
1106
  const notifyResult = options.notify ? installNotifications(root) : null;
1073
1107
  const legacyDetail = removedLegacy === 0 ? '' : ` · ${removedLegacy} legacy removed`;
1074
1108
  const statusDetail = configDetail(statusLineResult, notifyResult);
1109
+ if (groundAfter) applyStoredGround(home, groundAfter);
1075
1110
  progress.finish('ok', 'WRITE', `${installed} written · ${states.counts.current} already-current · ${states.counts.owned} owned${legacyDetail}${statusDetail}`, elapsedSince(writeStarted));
1076
1111
  } catch (error) {
1077
1112
  progress.finish('failed', 'WRITE', errorText(error), elapsedSince(writeStarted));
@@ -1086,6 +1121,8 @@ export async function run(args, context = {}) {
1086
1121
  progress.close();
1087
1122
  reportFailure(error, { stderr, stdout, options, root: displayRoot, colors, step });
1088
1123
  return 1;
1124
+ } finally {
1125
+ cleanupGround();
1089
1126
  }
1090
1127
  }
1091
1128
 
@@ -1 +1 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" width="146" height="20" role="img" aria-label="local: 1.0.28"><title>local: 1.0.28</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.28</text></g></svg>
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="146" height="20" role="img" aria-label="local: 1.0.30"><title>local: 1.0.30</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.30</text></g></svg>
package/docs/reference.md CHANGED
@@ -80,7 +80,7 @@ npm exec --yes --package @litfamily/litgrok@latest -- litgrok install
80
80
  Pin a version when you need a reproducible install:
81
81
 
82
82
  ```bash
83
- npm exec --yes --package @litfamily/litgrok@1.0.28 -- litgrok install
83
+ npm exec --yes --package @litfamily/litgrok@1.0.30 -- litgrok install
84
84
  ```
85
85
 
86
86
  Preview without writing files:
@@ -103,7 +103,7 @@ npm exec --yes --package @litfamily/litgrok@latest -- litgrok install --user
103
103
  npm exec --yes --package @litfamily/litgrok@latest -- litgrok doctor
104
104
  ```
105
105
 
106
- `doctor` is read-only. It prints the package version, asks the npm registry for the latest stable `@litfamily/litgrok` with a 3-second limit, and adds `newer version available: <version> (run: npm exec --yes --package @litfamily/litgrok@latest -- litgrok install)` when one exists. It prints nothing more when you are current, and one `update check unavailable` line when the registry cannot be reached. It never installs or writes files. `NO_UPDATE_NOTIFIER`, `CI` or `LITGROK_NO_UPDATE_CHECK` (present, even empty) skips the registry request.
106
+ `doctor` is read-only. It prints the package version and one `GROUND:` line for the section at the end of `~/.grok/rules/00-litgrok.md`: `present v=<version>`, `stale v=<version>`, `absent`, `malformed` or `off`. It asks the npm registry for the latest stable `@litfamily/litgrok` with a 3-second limit, and adds `newer version available: <version> (run: npm exec --yes --package @litfamily/litgrok@latest -- litgrok install)` when one exists. It adds nothing further when you are current, and one `update check unavailable` line when the registry cannot be reached. It never installs or writes files. `NO_UPDATE_NOTIFIER`, `CI` or `LITGROK_NO_UPDATE_CHECK` (present, even empty) skips the registry request. `litgrok ground off` removes that section and keeps it off; `litgrok ground on` restores it; `install --no-ground` skips it for that run. `litgrok ground project` writes the repo copy at `.grok/rules/10-litgrok-ground.md` and refuses a symlink, a non-directory path, or a file it does not own.
107
107
 
108
108
  ### Persistent status line (opt-in)
109
109
 
@@ -81,7 +81,7 @@ npm exec --yes --package @litfamily/litgrok@latest -- litgrok install
81
81
  재현 가능한 설치는 버전을 고정합니다.
82
82
 
83
83
  ```bash
84
- npm exec --yes --package @litfamily/litgrok@1.0.28 -- litgrok install
84
+ npm exec --yes --package @litfamily/litgrok@1.0.30 -- litgrok install
85
85
  ```
86
86
 
87
87
  파일을 쓰지 않고 미리보기:
@@ -104,7 +104,7 @@ npm exec --yes --package @litfamily/litgrok@latest -- litgrok install --user
104
104
  npm exec --yes --package @litfamily/litgrok@latest -- litgrok doctor
105
105
  ```
106
106
 
107
- `doctor`는 읽기만 합니다. 패키지 버전을 출력하고, npm 레지스트리에 최신 안정 `@litfamily/litgrok` 버전을 최대 3초 동안 물어봅니다. 더 새 버전이 있으면 `newer version available: <version> (run: npm exec --yes --package @litfamily/litgrok@latest -- litgrok install)` 한 줄을 더합니다. 최신이면 아무것도 더하지 않고, 레지스트리에 닿지 못하면 `update check unavailable` 한 줄만 출력합니다. 설치하거나 파일을 쓰지 않습니다. `NO_UPDATE_NOTIFIER`, `CI`, `LITGROK_NO_UPDATE_CHECK` 중 하나가 있으면(값이 비어 있어도) 레지스트리에 묻지 않습니다.
107
+ `doctor`는 읽기만 합니다. 패키지 버전을 출력하고, `~/.grok/rules/00-litgrok.md` 끝 구간에 대해 `GROUND:` 한 줄을 냅니다. 값은 `present v=<version>`, `stale v=<version>`, `absent`, `malformed`, `off` 중 하나입니다. npm 레지스트리에 최신 안정 `@litfamily/litgrok` 버전을 최대 3초 동안 물어봅니다. 더 새 버전이 있으면 `newer version available: <version> (run: npm exec --yes --package @litfamily/litgrok@latest -- litgrok install)` 한 줄을 더합니다. 최신이면 그 이상은 더하지 않고, 레지스트리에 닿지 못하면 `update check unavailable` 한 줄을 출력합니다. 설치하거나 파일을 쓰지 않습니다. `NO_UPDATE_NOTIFIER`, `CI`, `LITGROK_NO_UPDATE_CHECK` 중 하나가 있으면(값이 비어 있어도) 레지스트리에 묻지 않습니다. `litgrok ground off`는 그 구간을 빼고 꺼 둔 채로 두며, `litgrok ground on`은 다시 넣습니다. `install --no-ground`는 그 한 번만 건너뜁니다. `litgrok ground project`는 저장소 복사본을 `.grok/rules/10-litgrok-ground.md`에 쓰며, 심볼릭 링크, 디렉터리가 아닌 경로, 자신이 소유하지 않은 파일은 거절합니다.
108
108
 
109
109
  ### 지속 상태 행 (선택)
110
110
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@litfamily/litgrok",
3
- "version": "1.0.28",
3
+ "version": "1.0.30",
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.28",
3
+ "version": "1.0.30",
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"