@litfamily/litgrok 1.0.2 → 1.0.4
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/.grok/hooks/lit-status-line.mjs +111 -0
- package/.grok/hooks/litgrok-hud-state.mjs +126 -0
- package/.grok/hooks/record-passive-event.mjs +33 -9
- package/.grok/hooks/session-start.mjs +0 -16
- package/.grok/hooks/stop.mjs +18 -26
- package/.grok/skills/autoconference/SKILL.md +7 -4
- package/.grok/skills/autoresearch/SKILL.md +7 -4
- package/.grok/skills/lit-handoff/SKILL.md +8 -4
- package/.grok/skills/lit-scientific-visualization/SKILL.md +6 -3
- package/.grok/skills/litgoal/SKILL.md +1 -1
- package/.grok/skills/litgrok/SKILL.md +6 -4
- package/.grok/skills/litwork/SKILL.md +7 -4
- package/.grok-plugin/plugin.json +1 -2
- package/CHANGELOG.md +16 -0
- package/README.md +26 -12
- package/README_ko-KR.md +26 -12
- package/bin/litgrok.mjs +49 -18
- package/bin/status-line-config.mjs +184 -0
- package/docs/assets/cover-motion.webp +0 -0
- package/docs/assets/readme/badge-version.svg +1 -1
- package/docs/privacy.md +1 -3
- package/docs/reference.md +31 -27
- package/docs/reference_ko-KR.md +31 -27
- package/package.json +3 -1
- package/plugin.json +1 -1
- package/.grok/skills/skill-observer/SKILL.md +0 -78
- package/.grok/skills/skill-observer/references/review-contract.md +0 -77
- package/.grok/skills/skill-observer/scripts/curator.mjs +0 -121
- package/.grok/skills/skill-observer/scripts/review.mjs +0 -347
- package/.grok/skills/skill-observer/scripts/skill-loop.mjs +0 -2087
- package/.grok/skills/skill-observer/scripts/validate-skills.mjs +0 -83
package/README.md
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
|
-
<p align="center"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.
|
|
1
|
+
<p align="center"><picture><source media="(prefers-reduced-motion: reduce)" srcset="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.4/docs/assets/cover.webp" /><img src="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.4/docs/assets/cover-motion.webp" width="100%" alt="LitGrok cover art. An orange signal moves through orbital paths above the GROK BUILD wordmark. The static family cover appears for reduced-motion preference." /></picture></p>
|
|
2
|
+
<p align="center"><a href="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.4/docs/assets/cover.webp">View the static cover</a></p>
|
|
2
3
|
|
|
3
4
|
<h1 align="center">LitGrok</h1>
|
|
4
5
|
<p align="center"><strong>Keep the work lit.</strong></p>
|
|
5
|
-
<p align="center"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.
|
|
6
|
-
<p align="center"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.
|
|
6
|
+
<p align="center"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.4/docs/assets/litgrok-wordmark.svg" width="480" alt="LITGROK display type" /></p>
|
|
7
|
+
<p align="center"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.4/docs/assets/litgrok-clay-icon.png" width="160" alt="LitGrok clay mark" /></p>
|
|
7
8
|
|
|
8
9
|
Make something useful in Grok Build. Leave the checked result and the next step with your project.
|
|
9
10
|
|
|
10
11
|
[한국어](./README_ko-KR.md) · [Install](#install-in-30-seconds) · [First use](#first-lit-use) · [Reference](./docs/reference.md)
|
|
11
12
|
|
|
12
13
|
<p align="center">
|
|
13
|
-
<a href="#install-in-30-seconds"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.
|
|
14
|
-
<a href="./LICENSE"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.
|
|
14
|
+
<a href="#install-in-30-seconds"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.4/docs/assets/readme/badge-version.svg" alt="1.0.4" /></a>
|
|
15
|
+
<a href="./LICENSE"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.4/docs/assets/readme/badge-license.svg" alt="MIT license" /></a>
|
|
15
16
|
</p>
|
|
16
17
|
|
|
17
18
|
<p align="center">
|
|
@@ -26,9 +27,9 @@ Make something useful in Grok Build. Leave the checked result and the next step
|
|
|
26
27
|
>
|
|
27
28
|
> **Leave something the next session can pick up.**
|
|
28
29
|
|
|
29
|
-
It ships
|
|
30
|
+
It ships 33 skills, 11 agents, one project rule, and eleven hook registrations. The installer copies the complete payload into your project; Grok Build runs the session and owns model selection.
|
|
30
31
|
|
|
31
|
-
Package: **@litfamily/litgrok** · Version: **1.0.
|
|
32
|
+
Package: **@litfamily/litgrok** · Version: **1.0.4** · [MIT](./LICENSE)
|
|
32
33
|
|
|
33
34
|
The scientific visualization corpus is packaged at `.grok/vendor/scientific-visualization/`; the numeric `045_scientific-visualization` label remains only in license and provenance filenames.
|
|
34
35
|
|
|
@@ -52,7 +53,13 @@ Grok Build uses the explicit `/litwork` route for a bounded task. It owns model
|
|
|
52
53
|
|
|
53
54
|
In Grok Build, `/litwork` starts the bounded work checklist; `handoff` or `/lit-handoff` carries the checked result and next step; `/lit-plan` records a plan with checks; `/start-work <approved-plan>` executes that plan; `/review-work` inspects the change and its evidence; and `/litresearch` runs bounded, source-traceable research. The expected effect is a resumable, checked project record. The host limit is explicit: Grok Build owns execution and model selection, while LitGrok installs the `.grok` payload; hooks require a trusted Git root and a plain folder may load skills and rules without hooks. These images are static route cues, not proof of live host behavior.
|
|
54
55
|
|
|
55
|
-
|
|
56
|
+
The five activation contracts ask the model to start each activated response with exactly one line before other response content. For `/litwork`, the requested line is:
|
|
57
|
+
|
|
58
|
+
🔥 **LIT IGNITED · litwork** 🔥
|
|
59
|
+
|
|
60
|
+
This is advisory prompt guidance; it does not verify native host display.
|
|
61
|
+
|
|
62
|
+
<p align="center"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.4/docs/assets/litgrok-ignition-1600.webp" width="48%" alt="LitGrok ignition apparatus" /><img src="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.4/docs/assets/litgrok-continuity-1600.webp" width="48%" alt="LitGrok continuity apparatus" /></p>
|
|
56
63
|
|
|
57
64
|
<p align="center"><a href="./docs/assets/readme/ignition-film.mp4"><img src="./docs/assets/readme/ignition-poster.png" width="720" alt="Ignition motion poster" /></a></p>
|
|
58
65
|
|
|
@@ -75,7 +82,15 @@ LITGROK_PACK='/absolute/path/to/the-provided-package.tgz'
|
|
|
75
82
|
npm exec --yes --package "$LITGROK_PACK" -- litgrok install
|
|
76
83
|
```
|
|
77
84
|
|
|
78
|
-
The default destination is `<project>/.grok/`. Use `--package @litfamily/litgrok@1.0.
|
|
85
|
+
The default destination is `<project>/.grok/`. Use `--package @litfamily/litgrok@1.0.4` for a pinned install, or add `--user` to install under `~/.grok/`. [Install and upgrade details](./docs/reference.md#install).
|
|
86
|
+
|
|
87
|
+
The persistent Grok status row is optional and user-level only. Opt in with:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
npm exec --yes --package @litfamily/litgrok@latest -- litgrok install --user --status-line
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
This adds `[ui.status_line]` to `~/.grok/config.toml` with a two-second refresh. If a config already exists, LitGrok backs it up before changing it; an existing `ui.status_line` is preserved. `uninstall --user` removes only the unchanged LitGrok-managed value. Grok reads this setting from user or administrator configuration, not from project or plugin configuration ([status-line docs](https://docs.x.ai/build/features/status-line), [settings reference](https://docs.x.ai/build/settings/reference)). Example rows are `🔥 LIT IGNITED · lit-plan 🔥 │ grok-4 │ ctx 42%` and `LIT · grok │ grok-4 │ ctx 42%`. With color enabled, the active `LIT IGNITED · <discipline>` label is bold with a per-character truecolor gradient `#FF6337 → #FF2D95 → #00E5FF`; the flames and model/context segment remain unstyled. `NO_COLOR` (including an empty value) or `LITGROK_HUD_COLOR=0` keeps the row plain. Truecolor, bold and emoji rendering in the status row were confirmed on Grok Build 1.0.13. This adds a status command, not another hook registration; LitGrok still ships eleven hook registrations.
|
|
79
94
|
|
|
80
95
|
Project hooks need a trusted Git project root in Grok Build. A plain folder can still load the installed skills and rules while hooks remain unavailable. For a new disposable project, run `git init` yourself before trusting it; for an existing repository, open Grok Build from its actual root. The installer never runs `git init` or changes trust. In the observed Grok Build 1.0.23 host, `grok --trust inspect --json` accepted the trust-and-inspect route even though `--trust` was omitted from `grok --help`; this is version-specific behavior.
|
|
81
96
|
|
|
@@ -142,7 +157,7 @@ The hooks' `.grok/litgrok/session-ledger/` records event order. Judge success fr
|
|
|
142
157
|
| Review a change and its evidence | [`/review-work`](./.grok/skills/review-work/SKILL.md) |
|
|
143
158
|
| Leave a resumable handoff | [`/lit-handoff`](./.grok/skills/lit-handoff/SKILL.md) |
|
|
144
159
|
|
|
145
|
-
Use `/skills` for all
|
|
160
|
+
Use `/skills` for all 33 skills. [Rename compatibility](./docs/reference.md#skill-rename-compatibility) covers the one-release aliases; old slash routes are not guaranteed.
|
|
146
161
|
|
|
147
162
|
## Safety and uninstall
|
|
148
163
|
|
|
@@ -167,12 +182,11 @@ npm exec --yes --package @litfamily/litgrok@latest -- litgrok uninstall
|
|
|
167
182
|
npm exec --yes --package @litfamily/litgrok@latest -- litgrok uninstall --user
|
|
168
183
|
```
|
|
169
184
|
|
|
170
|
-
Match the scope used for installation. Re-run `install` to update. The installer does not grant hook trust, create a Git root, write login state or API keys, or choose a model. The hedge guard is fail-open on host errors; a passing package test does not prove live Grok behavior. Check `/hooks` and `grok inspect --json` from the repository root in your own session. [Host boundaries and verification](./docs/reference.md#verify).
|
|
185
|
+
User-level uninstall backs up `~/.grok/config.toml` before removing the unchanged LitGrok-managed status-line keys; other settings and a user-edited status line are preserved. Match the scope used for installation. Re-run `install` to update. The installer does not grant hook trust, create a Git root, write login state or API keys, or choose a model. The hedge guard is fail-open on host errors; a passing package test does not prove live Grok behavior. Check `/hooks` and `grok inspect --json` from the repository root in your own session. [Host boundaries and verification](./docs/reference.md#verify).
|
|
171
186
|
|
|
172
187
|
## Documentation
|
|
173
188
|
|
|
174
189
|
- [Operational reference](./docs/reference.md): installation, hooks, terminal output, payload checks, and source provenance.
|
|
175
|
-
- [Skill learning loop](./docs/reference.md#skill-learning-loop): proposal review, explicit apply, rollback, and macOS/Linux support. Windows learning-loop commands are unsupported.
|
|
176
190
|
- [Project rule](./.grok/rules/00-litgrok.md) and [skill catalog](./.grok/skills/).
|
|
177
191
|
- [Changelog](./CHANGELOG.md) and [license](./LICENSE).
|
|
178
192
|
|
package/README_ko-KR.md
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
|
-
<p align="center"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.
|
|
1
|
+
<p align="center"><picture><source media="(prefers-reduced-motion: reduce)" srcset="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.4/docs/assets/cover.webp" /><img src="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.4/docs/assets/cover-motion.webp" width="100%" alt="LitGrok cover art. An orange signal moves through orbital paths above the GROK BUILD wordmark. The static family cover appears for reduced-motion preference." /></picture></p>
|
|
2
|
+
<p align="center"><a href="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.4/docs/assets/cover.webp">정지 표지 보기</a></p>
|
|
2
3
|
|
|
3
4
|
<h1 align="center">LitGrok</h1>
|
|
4
5
|
<p align="center"><strong>Keep the work lit.</strong></p>
|
|
5
|
-
<p align="center"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.
|
|
6
|
-
<p align="center"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.
|
|
6
|
+
<p align="center"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.4/docs/assets/litgrok-wordmark.svg" width="480" alt="LITGROK 디스플레이 타입" /></p>
|
|
7
|
+
<p align="center"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.4/docs/assets/litgrok-clay-icon.png" width="160" alt="LitGrok 클레이 마크" /></p>
|
|
7
8
|
|
|
8
9
|
Grok Build에서 작은 결과물을 만들고, 확인한 내용과 다음 할 일을 프로젝트에 남기세요.
|
|
9
10
|
|
|
10
11
|
[English](./README.md) · [설치](#30초-설치) · [처음 사용하기](#첫-lit-작업) · [상세 안내](./docs/reference_ko-KR.md)
|
|
11
12
|
|
|
12
13
|
<p align="center">
|
|
13
|
-
<a href="#30초-설치"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.
|
|
14
|
-
<a href="./LICENSE"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.
|
|
14
|
+
<a href="#30초-설치"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.4/docs/assets/readme/badge-version.svg" alt="1.0.4" /></a>
|
|
15
|
+
<a href="./LICENSE"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.4/docs/assets/readme/badge-license.svg" alt="MIT 라이선스" /></a>
|
|
15
16
|
</p>
|
|
16
17
|
|
|
17
18
|
<p align="center">
|
|
@@ -26,9 +27,9 @@ Grok Build에서 작은 결과물을 만들고, 확인한 내용과 다음 할
|
|
|
26
27
|
>
|
|
27
28
|
> **대화가 끝난 자리에서, 다음 작업이 시작되도록.**
|
|
28
29
|
|
|
29
|
-
skill
|
|
30
|
+
skill 33개, agent 11개, project rule 하나, hook 등록 열한 개를 제공합니다. Installer가 프로젝트에 전체 파일을 복사하고, 세션 실행과 모델 선택은 Grok Build가 담당합니다.
|
|
30
31
|
|
|
31
|
-
패키지: **@litfamily/litgrok** · 버전: **1.0.
|
|
32
|
+
패키지: **@litfamily/litgrok** · 버전: **1.0.4** · [MIT](./LICENSE)
|
|
32
33
|
|
|
33
34
|
과학 시각화 corpus는 `.grok/vendor/scientific-visualization/`에 포함되며, 숫자 `045_scientific-visualization` 표기는 라이선스와 provenance 파일명에만 남습니다.
|
|
34
35
|
|
|
@@ -52,7 +53,13 @@ Grok Build에서는 명시적인 `/litwork` 경로로 작은 작업을 시작합
|
|
|
52
53
|
|
|
53
54
|
Grok Build에서 `/litwork`는 범위를 정한 작업 체크리스트를 시작하고, `handoff` 또는 `/lit-handoff`는 확인한 결과와 다음 단계를 건네며, `/lit-plan`은 확인 기준이 있는 계획을 남기고, `/start-work <승인된 계획>`은 그 계획을 실행하며, `/review-work`는 변경과 근거를 검토하고, `/litresearch`는 출처를 추적할 수 있는 범위 있는 조사를 합니다. 기대하는 효과는 확인 결과를 남기고 다음 세션에서 이어갈 수 있는 프로젝트 기록입니다. 호스트 제약도 분명합니다. 실행과 모델 선택은 Grok Build가 담당하고 LitGrok은 `.grok` payload를 설치합니다. Hook은 신뢰한 Git root에서만 제공되며, 일반 폴더에서는 skill과 rule만 로드되고 hook은 없을 수 있습니다. 이 이미지는 실제 호스트 동작을 증명하지 않는 정적 경로 안내입니다.
|
|
54
55
|
|
|
55
|
-
|
|
56
|
+
다섯 activation 계약은 모델이 활성 응답을 다른 내용보다 먼저 한 줄로 시작하도록 요청합니다. `/litwork`에서 요청하는 줄은 다음과 같습니다.
|
|
57
|
+
|
|
58
|
+
🔥 **LIT IGNITED · litwork** 🔥
|
|
59
|
+
|
|
60
|
+
이는 advisory prompt 지침이며 native host 표시 여부를 확인하지 않습니다.
|
|
61
|
+
|
|
62
|
+
<p align="center"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.4/docs/assets/litgrok-ignition-1600.webp" width="48%" alt="LitGrok 점화 장치" /><img src="https://cdn.jsdelivr.net/npm/@litfamily/litgrok@1.0.4/docs/assets/litgrok-continuity-1600.webp" width="48%" alt="LitGrok 연속 장치" /></p>
|
|
56
63
|
|
|
57
64
|
<p align="center"><a href="./docs/assets/readme/ignition-film.mp4"><img src="./docs/assets/readme/ignition-poster.png" width="720" alt="Ignition 모션 포스터" /></a></p>
|
|
58
65
|
|
|
@@ -75,7 +82,15 @@ LITGROK_PACK='/absolute/path/to/the-provided-package.tgz'
|
|
|
75
82
|
npm exec --yes --package "$LITGROK_PACK" -- litgrok install
|
|
76
83
|
```
|
|
77
84
|
|
|
78
|
-
기본 설치 위치는 `<project>/.grok/`입니다. 버전을 고정하려면 `--package @litfamily/litgrok@1.0.
|
|
85
|
+
기본 설치 위치는 `<project>/.grok/`입니다. 버전을 고정하려면 `--package @litfamily/litgrok@1.0.4`를 사용하세요. `--user`를 붙이면 `~/.grok/`에 설치합니다. [설치·업데이트 상세 안내](./docs/reference_ko-KR.md#설치).
|
|
86
|
+
|
|
87
|
+
Grok 상태 행의 지속 표시는 선택 사항이며 사용자 범위에서만 설정할 수 있습니다.
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
npm exec --yes --package @litfamily/litgrok@latest -- litgrok install --user --status-line
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
이 명령은 2초 간격으로 갱신되는 `[ui.status_line]`을 `~/.grok/config.toml`에 추가합니다. 기존 config를 바꾸기 전에 백업하며, 이미 `ui.status_line` 설정이 있으면 보존합니다. `uninstall --user`는 변경되지 않은 LitGrok 관리 값만 제거합니다. Grok은 이 설정을 사용자 또는 관리자 config에서 읽으며 project/plugin config에서는 읽지 않습니다 ([상태 행 문서](https://docs.x.ai/build/features/status-line), [설정 참조](https://docs.x.ai/build/settings/reference)). 예시는 `🔥 LIT IGNITED · lit-plan 🔥 │ grok-4 │ ctx 42%`와 `LIT · grok │ grok-4 │ ctx 42%`입니다. 색상을 사용하면 활성 `LIT IGNITED · <discipline>` label을 굵은 글씨로 문자마다 truecolor gradient(`#FF6337 → #FF2D95 → #00E5FF`)를 적용하고, 불꽃 emoji와 model/context 구간은 색칠하지 않습니다. 빈 값을 포함한 `NO_COLOR` 또는 `LITGROK_HUD_COLOR=0`은 escape 없는 plain 행을 유지합니다. Grok Build 1.0.13 상태 행에서 truecolor, 굵은 글씨, emoji가 표시되는 것을 확인했습니다. 이 기능은 status command를 추가하며 hook 등록 수를 늘리지 않습니다. LitGrok에는 계속 11개 hook 등록이 있습니다.
|
|
79
94
|
|
|
80
95
|
Project hook은 Grok Build에서 신뢰한 Git 프로젝트 루트가 있어야 발견됩니다. 일반 폴더에서도 설치된 skill과 rule은 로드될 수 있지만 hook은 0개로 남을 수 있습니다. 새 disposable project라면 사용자가 직접 `git init`을 실행한 뒤 trust를 허용하세요. 기존 저장소라면 실제 root에서 Grok Build를 여세요. Installer는 `git init`을 실행하거나 trust를 바꾸지 않습니다. 관찰된 Grok Build 1.0.23에서는 `grok --trust inspect --json`이 동작했지만 `grok --help`에는 global `--trust`가 표시되지 않았습니다. 이는 이 버전에서 확인한 동작입니다.
|
|
81
96
|
|
|
@@ -142,7 +157,7 @@ Hook의 `.grok/litgrok/session-ledger/`는 이벤트가 발생한 순서를 남
|
|
|
142
157
|
| 변경 내용과 근거 검토 | [`/review-work`](./.grok/skills/review-work/SKILL.md) |
|
|
143
158
|
| 다음 세션을 위한 인수인계 | [`/lit-handoff`](./.grok/skills/lit-handoff/SKILL.md) |
|
|
144
159
|
|
|
145
|
-
전체 skill
|
|
160
|
+
전체 skill 33개는 `/skills`에서 확인하세요. 이전 이름을 한 릴리스 동안 별칭으로 유지하는 정책은 [이름 변경과 호환성](./docs/reference_ko-KR.md#skill-이름-변경과-호환성)을 보세요. 이전 slash 경로는 보장되지 않습니다.
|
|
146
161
|
|
|
147
162
|
## 안전과 제거
|
|
148
163
|
|
|
@@ -167,12 +182,11 @@ npm exec --yes --package @litfamily/litgrok@latest -- litgrok uninstall
|
|
|
167
182
|
npm exec --yes --package @litfamily/litgrok@latest -- litgrok uninstall --user
|
|
168
183
|
```
|
|
169
184
|
|
|
170
|
-
|
|
185
|
+
사용자 범위 제거는 `~/.grok/config.toml`을 먼저 백업하고, 변경되지 않은 LitGrok 관리 status-line key만 제거합니다. 다른 설정과 사용자가 수정한 status line은 보존합니다. 설치 범위와 같은 옵션으로 제거하세요. 업데이트는 `install`을 다시 실행합니다. Installer는 Git root를 만들거나 hook trust를 허용하거나 로그인 상태·API 키를 쓰거나 모델을 선택하지 않습니다. Hedge guard는 host 오류 시 fail-open으로 동작합니다. 패키지 테스트가 통과해도 live Grok 동작을 확인한 것은 아닙니다. Git root에서 현재 세션의 `/hooks`와 `grok inspect --json`을 확인하세요. [Host 경계와 검증](./docs/reference_ko-KR.md#검증).
|
|
171
186
|
|
|
172
187
|
## 문서
|
|
173
188
|
|
|
174
189
|
- [상세 안내](./docs/reference_ko-KR.md): 설치, hook, 터미널 출력, 패키지 검증, 출처.
|
|
175
|
-
- [Skill learning loop](./docs/reference_ko-KR.md#skill-learning-loop): proposal 검토, 명시적 적용, rollback, macOS/Linux 지원. Windows의 learning-loop 명령은 지원하지 않습니다.
|
|
176
190
|
- [Project rule](./.grok/rules/00-litgrok.md)과 [skill 목록](./.grok/skills/).
|
|
177
191
|
- [변경 이력](./CHANGELOG.md)과 [라이선스](./LICENSE).
|
|
178
192
|
|
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 { installStatusLine, removeStatusLine } from './status-line-config.mjs';
|
|
9
10
|
|
|
10
11
|
const PACKAGE_ROOT = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
11
12
|
const PAYLOAD_ROOT = join(PACKAGE_ROOT, '.grok');
|
|
@@ -46,6 +47,7 @@ const PRE_MANIFEST_PAYLOAD_HASHES = Object.freeze({
|
|
|
46
47
|
'agents/litgrok-quality-reviewer.md': 'ed6f2687770f0f31c6821f0ad49d717362807f0bb5f4f27cf4d36be1e7dfd5a2',
|
|
47
48
|
'hooks/record-passive-event.mjs': '9f5bd1d6c8b8e6c0be4cc61cf31f0e26f4f8a41fceb826e88567f22efa2b0090',
|
|
48
49
|
'hooks/session-start.mjs': '5706ccce58340e78e435b9c1fd2224e101872ce1d0f51a9d187feb31b67c3b5d',
|
|
50
|
+
'hooks/stop.mjs': 'c796f02ac3dc99716d0c2f478bf93ec35cd8a9c725de18d12895957ee261b9c3',
|
|
49
51
|
'rules/00-litgrok.md': 'd46d1672e80250c8748458afa9b0fc1a46c2a318d31c4d58ab1ebc3f8f2eb804',
|
|
50
52
|
'skills/autoconference/SKILL.md': '025e9420ed9986b0466a8d660a4ed7f3e2d2243051ed3622be8ca87cbeba9458',
|
|
51
53
|
'skills/autoconference/references/_canonical-corpus/manifest.json': '4554ee4041b73577a844abc27a97db91e941b8412f1888209203251dc1fee500',
|
|
@@ -59,6 +61,7 @@ const PRE_MANIFEST_PAYLOAD_HASHES = Object.freeze({
|
|
|
59
61
|
'skills/lit-handoff/SKILL.md': '7152d6399fd778ab3d16e1abad2ec97e2f4be4350cd3993cdde038cc1211dd80',
|
|
60
62
|
'skills/lit-scientific-visualization/SKILL.md': 'ffc3b2ea49dd7e7c4c7d4568f289df294a3a30f953c11039f98aaf6f8fb983ee',
|
|
61
63
|
'skills/lit-scientific-visualization/scripts/verify-canonical-corpus.mjs': 'a1cfe0578195098dc4afc2d25f0d391f38aeaa57cd3970fa7dca51e20cbf929d',
|
|
64
|
+
'skills/litgoal/SKILL.md': 'e3fd76a8f4c91903ab85073b492f3981f76cb17644b840ba22d2a1c5e82178a9',
|
|
62
65
|
'skills/litgrok/SKILL.md': '6ea1fb66d5a1458e182cff5808ccc416487dbbad0341988bcda0f247645081e2',
|
|
63
66
|
'skills/litwork/SKILL.md': '771b7cead03c0d1d1e67af82e308ea0980b8ab18474026516798f559048beda7',
|
|
64
67
|
'skills/lsp-setup/SKILL.md': '313ffd924f00a1523905d2588e0812c8edcaec84b114d2d487506bd905e5376d',
|
|
@@ -66,8 +69,6 @@ const PRE_MANIFEST_PAYLOAD_HASHES = Object.freeze({
|
|
|
66
69
|
'skills/refactor/SKILL.md': '5fd2df1a36eae216f8af4db09e7e7d26231b167907ad19c49465153a1dcadde0',
|
|
67
70
|
'skills/review-work/SKILL.md': 'acfef3ff9c6e243cb9b88169a67e749e27a2e530af09aed07a2d66dd932c3b5d',
|
|
68
71
|
'skills/rules/SKILL.md': 'b880136962faeb3dea93cc42e30f5d3cc461a03fe71f7ff908d4b09170c38c5f',
|
|
69
|
-
'skills/skill-observer/scripts/skill-loop.mjs': '3f3f5265928c0d71175b0f23f71b836d1dc05e7efba407bfdc2d263c6aaed0ce',
|
|
70
|
-
'skills/skill-observer/scripts/validate-skills.mjs': 'cd71e88c45e57869114305f048257f3da7ec1dfa66f70d9529314defb5fa1a97',
|
|
71
72
|
'skills/start-work/SKILL.md': 'ddc7357f99c78cd3405c1cb0202f9b13871efbe59f365e9f39727c429791028b',
|
|
72
73
|
'skills/structural-search/SKILL.md': '3bbc13b5d0df58fbae07dd53e7ecbf13f311f12311cc3bd313803d63bd331ee0',
|
|
73
74
|
'skills/visual-qa/references/complete-contract.md': '9562a993b9fc75f8cda5e5f810ff534590f4ace36e696bf60119c0b27ba19cd5',
|
|
@@ -152,13 +153,19 @@ const PRE_MANIFEST_LEGACY_PAYLOAD_HASHES = Object.freeze({
|
|
|
152
153
|
'skills/programming/references/typescript/type-patterns.md': 'a21e27706f5d1e60be8f37d2d47949d0759158c999c4a08628f5f925b184d946',
|
|
153
154
|
'skills/programming/references/worked-cases.md': 'ccb6fb8011cda2391d73c4f85ffc3b8038486d77134b986b046d1f62a2f7a9d2',
|
|
154
155
|
'skills/remove-ai-slops/SKILL.md': 'db2f9e91dfd70a2077a091d1c422cba9f6d576af58868df4279d0e7542c29380',
|
|
156
|
+
'skills/skill-observer/SKILL.md': '1edf489673d958b3a7c60c5e1a036498510a02859dcf32e68a6d50b21f2fa344',
|
|
157
|
+
'skills/skill-observer/references/review-contract.md': 'f55dc8334f3b8d8498c84d57725fa03af8fbd689b2dd7cce25e49931629ee54f',
|
|
158
|
+
'skills/skill-observer/scripts/curator.mjs': '07f48d2160ba64584acd17ae9bd55efd6272546722576c288725b8850e3f0455',
|
|
159
|
+
'skills/skill-observer/scripts/review.mjs': '5b79fedae7f7c4d2b6d898717ae427bae1bd6d2c7f9f4bbc2f410f86468f7691',
|
|
160
|
+
'skills/skill-observer/scripts/skill-loop.mjs': '3f3f5265928c0d71175b0f23f71b836d1dc05e7efba407bfdc2d263c6aaed0ce',
|
|
161
|
+
'skills/skill-observer/scripts/validate-skills.mjs': 'cd71e88c45e57869114305f048257f3da7ec1dfa66f70d9529314defb5fa1a97',
|
|
155
162
|
'skills/teammode/SKILL.md': '23db7152e2ce65cb5f7c52455d6cbd122485bb0fdb13813a8a54b824e8e44f08',
|
|
156
163
|
'skills/teammode/references/explore-packet.md': 'c419c1c84783ddfae272e378c4f7e623ec4fbb437168d2d5cb8436baf4c14e30',
|
|
157
164
|
'skills/teammode/references/general-purpose-packet.md': '45ad9cf82157563ebb20af360bb0b09d7afd7aa0fe114a91816523475ebdc88c',
|
|
158
165
|
'skills/teammode/references/plan-packet.md': '68e292f9a6dc64f7e87a45417d5f92688aac9c2ffb7b519f9e2cab10b5ef4759',
|
|
159
166
|
'skills/text-naturalization/SKILL.md': 'aa1ff8f68ab0bf71cb47818b536c4a2934da65adb413ee43ec5839b540b2df8e',
|
|
160
167
|
});
|
|
161
|
-
const USAGE = 'Usage: litgrok [install|uninstall] [--user|--project] [--dry-run] [--no-color] [--yes]\n
|
|
168
|
+
const USAGE = 'Usage: litgrok [install|uninstall] [--user|--project] [--status-line] [--dry-run] [--no-color] [--yes]\n';
|
|
162
169
|
const BRAILLE_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
163
170
|
const SPINNER_INTERVAL_MS = 80;
|
|
164
171
|
const CLEAR_LINE = '\r\u001b[2K';
|
|
@@ -167,13 +174,15 @@ function parseInstall(args) {
|
|
|
167
174
|
const remaining = [...args];
|
|
168
175
|
let command = 'install';
|
|
169
176
|
if (['install', 'uninstall'].includes(remaining[0])) command = remaining.shift();
|
|
170
|
-
if (remaining.some((argument) => !['--user', '--project', '--dry-run', '--no-color', '--yes'].includes(argument))) return null;
|
|
177
|
+
if (remaining.some((argument) => !['--user', '--project', '--status-line', '--dry-run', '--no-color', '--yes'].includes(argument))) return null;
|
|
171
178
|
if (remaining.includes('--user') && remaining.includes('--project')) return null;
|
|
179
|
+
if (remaining.includes('--status-line') && (command !== 'install' || !remaining.includes('--user'))) return null;
|
|
172
180
|
return {
|
|
173
181
|
command,
|
|
174
182
|
dryRun: remaining.includes('--dry-run'),
|
|
175
183
|
noColor: remaining.includes('--no-color'),
|
|
176
184
|
yes: remaining.includes('--yes'),
|
|
185
|
+
statusLine: remaining.includes('--status-line'),
|
|
177
186
|
scope: remaining.includes('--user') ? 'user' : 'project',
|
|
178
187
|
};
|
|
179
188
|
}
|
|
@@ -324,7 +333,7 @@ function receiptCard(options, root, colors, { status, written, current }) {
|
|
|
324
333
|
].join('\n');
|
|
325
334
|
}
|
|
326
335
|
|
|
327
|
-
function failureCard(options, root, colors, step, message) {
|
|
336
|
+
function failureCard(options, root, colors, step, message, { showHookTrust = true } = {}) {
|
|
328
337
|
return [
|
|
329
338
|
'',
|
|
330
339
|
` ${paint('╭─ INSTALL FAILED', '38;2;230;60;60', colors)}`,
|
|
@@ -332,9 +341,11 @@ function failureCard(options, root, colors, step, message) {
|
|
|
332
341
|
` │ Scope: ${scopeDescription(options, root)}`,
|
|
333
342
|
` │ Step: ${step}`,
|
|
334
343
|
` │ Reason: ${message}`,
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
344
|
+
...(showHookTrust ? [
|
|
345
|
+
' │ Hooks: project hooks require trust from a Git project root',
|
|
346
|
+
' │ Trust: use /hooks-trust or, on Grok Build 1.0.13, grok --trust inspect --json',
|
|
347
|
+
' │ Installer never runs git init or changes trust',
|
|
348
|
+
] : []),
|
|
338
349
|
' │ Next: resolve the issue and rerun the install',
|
|
339
350
|
` ${paint('╰─', '38;2;230;60;60', colors)}`,
|
|
340
351
|
'',
|
|
@@ -348,7 +359,9 @@ function errorText(error) {
|
|
|
348
359
|
function reportFailure(error, { stderr, stdout, options, root, colors, step }) {
|
|
349
360
|
const lines = Array.isArray(error?.stderrLines) ? error.stderrLines : [`litgrok: ${errorText(error)}`];
|
|
350
361
|
for (const line of lines) stderr.write(`${line}\n`);
|
|
351
|
-
stdout.write(failureCard(options, root, colors, error?.step ?? step, errorText(error)
|
|
362
|
+
stdout.write(failureCard(options, root, colors, error?.step ?? step, errorText(error), {
|
|
363
|
+
showHookTrust: error?.legacyOwnershipConflict !== true,
|
|
364
|
+
}));
|
|
352
365
|
}
|
|
353
366
|
|
|
354
367
|
function normalizedRelativePath(relativePath) {
|
|
@@ -489,6 +502,12 @@ function retiredPayload(root, files, manifest) {
|
|
|
489
502
|
const hashes = new Map([...previous].filter(([path]) => !currentPaths.has(path) && retiredPath(path)));
|
|
490
503
|
for (const path of agentPaths) if (!hashes.has(path)) hashes.set(path, undefined);
|
|
491
504
|
const directories = [];
|
|
505
|
+
const retiredSkillRoots = new Set(
|
|
506
|
+
[...hashes.keys()]
|
|
507
|
+
.filter((path) => path.startsWith('skills/'))
|
|
508
|
+
.map((path) => path.split('/').slice(0, 2).join('/'))
|
|
509
|
+
.filter((path) => !currentPaths.has(`${path}/SKILL.md`)),
|
|
510
|
+
);
|
|
492
511
|
|
|
493
512
|
function visit(path) {
|
|
494
513
|
const target = join(root, path);
|
|
@@ -511,7 +530,7 @@ function retiredPayload(root, files, manifest) {
|
|
|
511
530
|
}
|
|
512
531
|
directories.push(target);
|
|
513
532
|
}
|
|
514
|
-
for (const path of skillRoots) visit(path);
|
|
533
|
+
for (const path of new Set([...skillRoots, ...retiredSkillRoots])) visit(path);
|
|
515
534
|
for (const path of vendorRoots) visit(path);
|
|
516
535
|
const entries = [...hashes].map(([relativePath, expectedHash]) => ({ root, relativePath, expectedHash, target: join(root, relativePath) }));
|
|
517
536
|
return { entries, directories };
|
|
@@ -690,10 +709,6 @@ export async function run(args, context = {}) {
|
|
|
690
709
|
stdout.write(`${renderMark({ size: 'banner', productName: `grok v${packageVersion()}`, env, isTTY: stdout.isTTY, args }).join('\n')}\n\n${USAGE}`);
|
|
691
710
|
return 0;
|
|
692
711
|
}
|
|
693
|
-
if (args[0] === 'skill-loop') {
|
|
694
|
-
const { runSkillLoop } = await import('../.grok/skills/skill-observer/scripts/skill-loop.mjs');
|
|
695
|
-
return runSkillLoop(args.slice(1), { cwd, env, stderr, stdout });
|
|
696
|
-
}
|
|
697
712
|
const options = parseInstall(args);
|
|
698
713
|
if (!options) {
|
|
699
714
|
stderr.write(USAGE);
|
|
@@ -746,6 +761,7 @@ export async function run(args, context = {}) {
|
|
|
746
761
|
if (reasons.length > 0) {
|
|
747
762
|
stdout.write(`${reasons.join(' / ')} — no files written\n`);
|
|
748
763
|
for (const { target } of files) stdout.write(`would ${options.command} ${target}\n`);
|
|
764
|
+
if (options.statusLine) stdout.write(`would configure user status line ${join(root, 'config.toml')}\n`);
|
|
749
765
|
printHookTrustRequirement(files);
|
|
750
766
|
return 0;
|
|
751
767
|
}
|
|
@@ -792,7 +808,13 @@ export async function run(args, context = {}) {
|
|
|
792
808
|
if (owned[index] === 'conflict') stderrLines.push(`Refusing to ${options.command === 'install' ? 'overwrite' : 'remove'} different existing file: ${files[index].target}`);
|
|
793
809
|
}
|
|
794
810
|
for (let index = 0; index < legacyEntries.length; index += 1) {
|
|
795
|
-
if (legacyStates[index]
|
|
811
|
+
if (legacyStates[index] !== 'conflict') continue;
|
|
812
|
+
const entry = legacyEntries[index];
|
|
813
|
+
const retiredSkill = retiredDirectories.some((directory) => (
|
|
814
|
+
directory.startsWith(`${join(root, 'skills')}${sep}`) && entry.target.startsWith(`${directory}${sep}`)
|
|
815
|
+
));
|
|
816
|
+
const retiredDirectory = retiredSkill ? 'the retired skill directory' : 'the retired payload directory';
|
|
817
|
+
stderrLines.push(`Refusing legacy conflict: ${entry.target} is not package-owned. Move it out of ${retiredDirectory} or remove it yourself, then rerun the install so the directory can be cleaned.`);
|
|
796
818
|
}
|
|
797
819
|
const legacyDetail = legacyEntries.length === 0
|
|
798
820
|
? ''
|
|
@@ -800,6 +822,7 @@ export async function run(args, context = {}) {
|
|
|
800
822
|
const error = new Error(`${counts.missing} missing · ${counts.current} already-current · ${counts.owned} owned · ${counts.conflicts} conflicts${legacyDetail}`);
|
|
801
823
|
error.step = 'OWNERSHIP';
|
|
802
824
|
error.stderrLines = stderrLines;
|
|
825
|
+
error.legacyOwnershipConflict = legacyCounts.conflicts > 0;
|
|
803
826
|
throw error;
|
|
804
827
|
}
|
|
805
828
|
const legacyDetail = legacyEntries.length === 0
|
|
@@ -816,7 +839,9 @@ export async function run(args, context = {}) {
|
|
|
816
839
|
const writeStarted = process.hrtime.bigint();
|
|
817
840
|
progress.start('WRITE');
|
|
818
841
|
let removed = 0;
|
|
842
|
+
let statusLineResult = null;
|
|
819
843
|
try {
|
|
844
|
+
if (options.scope === 'user') statusLineResult = removeStatusLine(root);
|
|
820
845
|
for (let index = 0; index < files.length; index += 1) {
|
|
821
846
|
if (!['current', 'owned'].includes(states.states[index])) continue;
|
|
822
847
|
unlinkSync(files[index].target);
|
|
@@ -826,7 +851,8 @@ export async function run(args, context = {}) {
|
|
|
826
851
|
removeRetiredDirectories(states.retiredDirectories);
|
|
827
852
|
removeEmptyPayloadDirectories(payloadDirectories(root, files));
|
|
828
853
|
removeOwnershipManifest(root, states.manifest);
|
|
829
|
-
|
|
854
|
+
const statusDetail = statusLineResult ? ` · ${statusLineResult.detail}` : '';
|
|
855
|
+
progress.finish('ok', 'WRITE', `${removed} removed · ${states.counts.missing} already absent${statusDetail}`, elapsedSince(writeStarted));
|
|
830
856
|
} catch (error) {
|
|
831
857
|
progress.finish('failed', 'WRITE', errorText(error), elapsedSince(writeStarted));
|
|
832
858
|
if (error && typeof error === 'object') error.step = 'WRITE';
|
|
@@ -839,13 +865,16 @@ export async function run(args, context = {}) {
|
|
|
839
865
|
}
|
|
840
866
|
|
|
841
867
|
if (states.states.every((state) => state === 'current') && states.legacyStates.every((state) => state === 'missing') && states.retiredDirectories.length === 0) {
|
|
868
|
+
let statusLineResult = null;
|
|
842
869
|
try {
|
|
843
870
|
writeOwnershipManifest(root, files, states.manifest);
|
|
871
|
+
if (options.statusLine) statusLineResult = installStatusLine(root);
|
|
844
872
|
} catch (error) {
|
|
845
873
|
if (error && typeof error === 'object') error.step = 'WRITE';
|
|
846
874
|
throw error;
|
|
847
875
|
}
|
|
848
|
-
|
|
876
|
+
const statusDetail = statusLineResult ? ` · ${statusLineResult.detail}` : '';
|
|
877
|
+
skipStep(progress, 'WRITE', `SKIPPED · 0 written · ${states.counts.current} already-current${statusDetail}`);
|
|
849
878
|
const receipt = receiptCard(options, displayRoot, colors, { status: 'Already current', written: 0, current: states.counts.current });
|
|
850
879
|
progress.close();
|
|
851
880
|
stdout.write(receipt);
|
|
@@ -867,8 +896,10 @@ export async function run(args, context = {}) {
|
|
|
867
896
|
const removedLegacy = removeLegacyFiles(states.legacyEntries);
|
|
868
897
|
removeRetiredDirectories(states.retiredDirectories);
|
|
869
898
|
writeOwnershipManifest(root, files, states.manifest);
|
|
899
|
+
const statusLineResult = options.statusLine ? installStatusLine(root) : null;
|
|
870
900
|
const legacyDetail = removedLegacy === 0 ? '' : ` · ${removedLegacy} legacy removed`;
|
|
871
|
-
|
|
901
|
+
const statusDetail = statusLineResult ? ` · ${statusLineResult.detail}` : '';
|
|
902
|
+
progress.finish('ok', 'WRITE', `${installed} written · ${states.counts.current} already-current · ${states.counts.owned} owned${legacyDetail}${statusDetail}`, elapsedSince(writeStarted));
|
|
872
903
|
} catch (error) {
|
|
873
904
|
progress.finish('failed', 'WRITE', errorText(error), elapsedSince(writeStarted));
|
|
874
905
|
if (error && typeof error === 'object') error.step = 'WRITE';
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { constants, copyFileSync, lstatSync, linkSync, readFileSync, renameSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { basename, dirname, join, resolve } from 'node:path';
|
|
4
|
+
|
|
5
|
+
const MARKER = '# litgrok-managed-status-line-v1';
|
|
6
|
+
const MAX_CONFIG_BYTES = 1024 * 1024;
|
|
7
|
+
|
|
8
|
+
function configError(code) {
|
|
9
|
+
const error = new Error(code);
|
|
10
|
+
error.code = code;
|
|
11
|
+
return error;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function readConfig(path) {
|
|
15
|
+
try {
|
|
16
|
+
const status = lstatSync(path);
|
|
17
|
+
if (!status.isFile() || status.isSymbolicLink() || status.size > MAX_CONFIG_BYTES) throw configError('STATUS_CONFIG_UNSAFE');
|
|
18
|
+
return { exists: true, mode: status.mode & 0o777, text: readFileSync(path, 'utf8') };
|
|
19
|
+
} catch (error) {
|
|
20
|
+
if (error?.code === 'ENOENT') return { exists: false, mode: 0o600, text: '' };
|
|
21
|
+
throw error;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function withoutComment(line) {
|
|
26
|
+
let quote = '';
|
|
27
|
+
let escaped = false;
|
|
28
|
+
for (let index = 0; index < line.length; index += 1) {
|
|
29
|
+
const character = line[index];
|
|
30
|
+
if (quote) {
|
|
31
|
+
if (quote === '"' && character === '\\' && !escaped) escaped = true;
|
|
32
|
+
else {
|
|
33
|
+
if (character === quote && !escaped) quote = '';
|
|
34
|
+
escaped = false;
|
|
35
|
+
}
|
|
36
|
+
} else if (character === '"' || character === "'") quote = character;
|
|
37
|
+
else if (character === '#') return line.slice(0, index);
|
|
38
|
+
}
|
|
39
|
+
return line;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function lineText(line) {
|
|
43
|
+
return withoutComment(line.replace(/\r?\n$/u, '').replace(/^\uFEFF/u, '')).trim();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function rawLineText(line) {
|
|
47
|
+
return line.replace(/\r?\n$/u, '').replace(/^\uFEFF/u, '').trim();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function splitLines(text) {
|
|
51
|
+
return text.match(/[^\n]*\n|[^\n]+$/gu) ?? [];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function tableHeader(line) {
|
|
55
|
+
return /^\[\s*ui\s*\.\s*status_line\s*\]$/u.test(lineText(line));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function managedBlock(text, command) {
|
|
59
|
+
const lines = splitLines(text);
|
|
60
|
+
const markers = lines.map((line, index) => rawLineText(line) === MARKER ? index : -1).filter((index) => index >= 0);
|
|
61
|
+
if (markers.length !== 1) return null;
|
|
62
|
+
const markerIndex = markers[0];
|
|
63
|
+
let headerIndex = markerIndex + 1;
|
|
64
|
+
while (headerIndex < lines.length && lineText(lines[headerIndex]) === '') headerIndex += 1;
|
|
65
|
+
if (!tableHeader(lines[headerIndex] ?? '')) return null;
|
|
66
|
+
let sectionEnd = headerIndex + 1;
|
|
67
|
+
while (sectionEnd < lines.length && !/^\s*\[\[?[^\]]+\]\]?\s*$/u.test(lines[sectionEnd].replace(/\r?\n$/u, ''))) sectionEnd += 1;
|
|
68
|
+
|
|
69
|
+
const values = new Map();
|
|
70
|
+
for (let index = headerIndex + 1; index < sectionEnd; index += 1) {
|
|
71
|
+
const code = lineText(lines[index]);
|
|
72
|
+
const assignment = code.match(/^([A-Za-z0-9_-]+)\s*=\s*(.*?)\s*$/u);
|
|
73
|
+
if (!assignment) continue;
|
|
74
|
+
const entries = values.get(assignment[1]) ?? [];
|
|
75
|
+
entries.push({ index, value: assignment[2] });
|
|
76
|
+
values.set(assignment[1], entries);
|
|
77
|
+
}
|
|
78
|
+
const type = values.get('type') ?? [];
|
|
79
|
+
const commandValues = values.get('command') ?? [];
|
|
80
|
+
const refresh = values.get('refresh_interval') ?? [];
|
|
81
|
+
if (type.length !== 1 || type[0].value !== '"command"'
|
|
82
|
+
|| commandValues.length !== 1 || commandValues[0].value !== JSON.stringify(command)
|
|
83
|
+
|| refresh.length !== 1 || refresh[0].value !== '2') return null;
|
|
84
|
+
return {
|
|
85
|
+
markerIndex,
|
|
86
|
+
headerIndex,
|
|
87
|
+
sectionEnd,
|
|
88
|
+
valueIndexes: [type[0].index, commandValues[0].index, refresh[0].index],
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function hasStatusLineSetting(text) {
|
|
93
|
+
return splitLines(text).some((line) => /\bstatus_line\b/u.test(lineText(line)));
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function shellQuote(value) {
|
|
97
|
+
if (/^[A-Za-z0-9_./-]+$/u.test(value)) return value;
|
|
98
|
+
return `'${value.replaceAll("'", "'\\''")}'`;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function commandValue(root) {
|
|
102
|
+
const script = resolve(root, 'hooks', 'lit-status-line.mjs');
|
|
103
|
+
return `node ${shellQuote(script)}`;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function backup(path) {
|
|
107
|
+
const target = `${path}.litgrok-backup-${randomUUID()}`;
|
|
108
|
+
copyFileSync(path, target, constants.COPYFILE_EXCL);
|
|
109
|
+
return target;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function writeConfig(path, previous, next) {
|
|
113
|
+
const directory = dirname(path);
|
|
114
|
+
const temporary = join(directory, `.${basename(path)}.${randomUUID()}.tmp`);
|
|
115
|
+
if (previous.exists) backup(path);
|
|
116
|
+
try {
|
|
117
|
+
writeFileSync(temporary, next, { flag: 'wx', mode: previous.mode });
|
|
118
|
+
if (previous.exists) {
|
|
119
|
+
if (readFileSync(path, 'utf8') !== previous.text) throw configError('STATUS_CONFIG_CHANGED');
|
|
120
|
+
renameSync(temporary, path);
|
|
121
|
+
} else {
|
|
122
|
+
linkSync(temporary, path);
|
|
123
|
+
unlinkSync(temporary);
|
|
124
|
+
}
|
|
125
|
+
} finally {
|
|
126
|
+
try {
|
|
127
|
+
unlinkSync(temporary);
|
|
128
|
+
} catch (error) {
|
|
129
|
+
if (error?.code !== 'ENOENT') throw error;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function statusLineBlock(command) {
|
|
135
|
+
return [
|
|
136
|
+
MARKER,
|
|
137
|
+
'[ui.status_line]',
|
|
138
|
+
'type = "command"',
|
|
139
|
+
`command = ${JSON.stringify(command)}`,
|
|
140
|
+
'refresh_interval = 2',
|
|
141
|
+
].join('\n');
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function appendBlock(text, block) {
|
|
145
|
+
if (!text) return `${block}\n`;
|
|
146
|
+
const separator = text.endsWith('\n\n') ? '' : text.endsWith('\n') ? '\n' : '\n\n';
|
|
147
|
+
return `${text}${separator}${block}\n`;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export function installStatusLine(root) {
|
|
151
|
+
const path = join(root, 'config.toml');
|
|
152
|
+
const command = commandValue(root);
|
|
153
|
+
const previous = readConfig(path);
|
|
154
|
+
if (managedBlock(previous.text, command)) return { changed: false, detail: 'already managed by LitGrok' };
|
|
155
|
+
if (previous.text.includes(MARKER)) return { changed: false, detail: 'modified LitGrok status-line config preserved' };
|
|
156
|
+
if (hasStatusLineSetting(previous.text)) return { changed: false, detail: 'existing user status_line preserved' };
|
|
157
|
+
const next = appendBlock(previous.text, statusLineBlock(command));
|
|
158
|
+
writeConfig(path, previous, next);
|
|
159
|
+
return { changed: true, detail: 'user status line configured' };
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function removeManagedBlock(text, block) {
|
|
163
|
+
const lines = splitLines(text);
|
|
164
|
+
const remove = new Set([block.markerIndex, ...block.valueIndexes]);
|
|
165
|
+
const remaining = [];
|
|
166
|
+
for (let index = block.headerIndex + 1; index < block.sectionEnd; index += 1) {
|
|
167
|
+
if (!remove.has(index) && lineText(lines[index]) !== '') remaining.push(index);
|
|
168
|
+
}
|
|
169
|
+
if (remaining.length === 0) remove.add(block.headerIndex);
|
|
170
|
+
return lines.filter((_, index) => !remove.has(index)).join('');
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export function removeStatusLine(root) {
|
|
174
|
+
const path = join(root, 'config.toml');
|
|
175
|
+
const command = commandValue(root);
|
|
176
|
+
const previous = readConfig(path);
|
|
177
|
+
if (!previous.exists) return { changed: false, detail: 'no user config found' };
|
|
178
|
+
const block = managedBlock(previous.text, command);
|
|
179
|
+
if (!block) return { changed: false, detail: 'no unchanged LitGrok status-line value found' };
|
|
180
|
+
const next = removeManagedBlock(previous.text, block);
|
|
181
|
+
if (next === previous.text) return { changed: false, detail: 'no LitGrok status-line value removed' };
|
|
182
|
+
writeConfig(path, previous, next);
|
|
183
|
+
return { changed: true, detail: 'LitGrok status-line value removed' };
|
|
184
|
+
}
|
|
Binary file
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="146" height="20" role="img" aria-label="local: 1.0.
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="146" height="20" role="img" aria-label="local: 1.0.4"><title>local: 1.0.4</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.4</text></g></svg>
|
package/docs/privacy.md
CHANGED
|
@@ -4,9 +4,7 @@ This describes package source behavior, not Grok or a model provider's privacy p
|
|
|
4
4
|
|
|
5
5
|
Passive hooks in `.grok/hooks/record-passive-event.mjs` read hook JSON and record session identity, cwd/workspace paths, event names and time, optional prompt IDs, and tool names with input digests. They inspect optional prompt text for planning activation without persisting raw prompt/tool input in those passive records. Hashed filenames do not anonymize the JSON: records retain the session ID. The hedge guard examines proposed paths and content; Stop may record a plan-gate reason.
|
|
6
6
|
|
|
7
|
-
Project `.grok/litgrok/`
|
|
8
|
-
|
|
9
|
-
An explicitly requested `skill-loop review` runs `grok export`, scrubs known secret patterns, bounds the transcript excerpt to 64 KiB, and passes it with eligible skill content and session/root references to `grok --single`. This source path is in `.grok/skills/skill-observer/scripts/review.mjs`. Grok may send that input to its configured provider. The prompt is also passed as a command-line argument and may be visible to local process inspection. Pattern-based scrubbing is not a guarantee that all sensitive information is removed. Inspect the source and data before requesting a review of sensitive work. Disabled review tools and web search do not imply offline model execution.
|
|
7
|
+
Project `.grok/litgrok/` contains package-owned session ledgers and may contain other generated data from installed or older package versions. Older skill-review state is no longer read, migrated, rewritten, or deleted by LitGrok; it remains inert for the owner to manage.
|
|
10
8
|
|
|
11
9
|
Optional helpers may invoke Grok or external scientific tools; their behavior and dependencies remain separate. No blanket claim that all package-guided work stays offline is made.
|
|
12
10
|
|