@lovrozagar/grunt 0.9.5 → 0.10.0
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/.agents/skills/jev/SKILL.md +18 -0
- package/.claude/skills/jev/SKILL.md +18 -0
- package/.grok/hooks/orchestrate-parent.js +9 -1
- package/.grok/skills/jev/SKILL.md +18 -0
- package/.rulesync/reference/INDEX.md +3 -1
- package/.rulesync/reference/hooks.md +1 -1
- package/.rulesync/reference/jev.md +56 -0
- package/.rulesync/reference/law.md +1 -1
- package/.rulesync/reference/refs-map.md +1 -0
- package/.rulesync/reference/setup.md +3 -1
- package/.rulesync/reference/skills-map.md +1 -0
- package/.rulesync/skills/jev/SKILL.md +15 -0
- package/CHANGELOG.md +7 -0
- package/README.md +19 -7
- package/cli/grunt.mjs +60 -8
- package/cli/grunt.test.ts +122 -1
- package/cli/init.mjs +28 -6
- package/cli/init.test.ts +104 -5
- package/cli/prompt.mjs +1 -0
- package/cli/prompt.test.ts +29 -0
- package/package.json +4 -1
- package/scripts/board.mjs +390 -0
- package/scripts/doctor.mjs +38 -13
- package/scripts/feature-packs.mjs +123 -0
- package/scripts/jev.mjs +355 -0
- package/scripts/prompt.mjs +4 -0
- package/scripts/session-map.mjs +3 -1
- package/scripts/setup.mjs +85 -47
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: jev
|
|
3
|
+
description: >-
|
|
4
|
+
Yes/no or named-option decision on text already saved. Run node
|
|
5
|
+
scripts/jev.mjs --available first. On yes, one decision call. On no, decide
|
|
6
|
+
yourself and do not mention Jev.
|
|
7
|
+
---
|
|
8
|
+
# jev
|
|
9
|
+
|
|
10
|
+
Use this when the source text is already saved and the leftover question is a yes/no or a choice among options the research already named. Spec: `.rulesync/reference/jev.md`.
|
|
11
|
+
|
|
12
|
+
Run `node scripts/jev.mjs --available`. It prints `yes` or `no` and exits 0.
|
|
13
|
+
|
|
14
|
+
`no`: answer the question yourself. Do not run a decision call. Do not mention Jev, a missing key, or setup.
|
|
15
|
+
|
|
16
|
+
`yes`: read `.rulesync/reference/jev.md`. Write the passage and one questions file under `.tmp/grunt/jev/`. Run `node scripts/jev.mjs --state <state> --questions <questions>` once. Use the printed label and confidence. A low confidence is the result. Do not call again.
|
|
17
|
+
|
|
18
|
+
Open research, code, and grunt-job stay with the session. If the decision call exits non-zero, answer from the sources. Do not invent a label.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: jev
|
|
3
|
+
description: >-
|
|
4
|
+
Yes/no or named-option decision on text already saved. Run node
|
|
5
|
+
scripts/jev.mjs --available first. On yes, one decision call. On no, decide
|
|
6
|
+
yourself and do not mention Jev.
|
|
7
|
+
---
|
|
8
|
+
# jev
|
|
9
|
+
|
|
10
|
+
Use this when the source text is already saved and the leftover question is a yes/no or a choice among options the research already named. Spec: `.rulesync/reference/jev.md`.
|
|
11
|
+
|
|
12
|
+
Run `node scripts/jev.mjs --available`. It prints `yes` or `no` and exits 0.
|
|
13
|
+
|
|
14
|
+
`no`: answer the question yourself. Do not run a decision call. Do not mention Jev, a missing key, or setup.
|
|
15
|
+
|
|
16
|
+
`yes`: read `.rulesync/reference/jev.md`. Write the passage and one questions file under `.tmp/grunt/jev/`. Run `node scripts/jev.mjs --state <state> --questions <questions>` once. Use the printed label and confidence. A low confidence is the result. Do not call again.
|
|
17
|
+
|
|
18
|
+
Open research, code, and grunt-job stay with the session. If the decision call exits non-zero, answer from the sources. Do not invent a label.
|
|
@@ -22,6 +22,7 @@ import { persistImplementation, isFullImplementationBody, validateImplementation
|
|
|
22
22
|
import { persistTmp } from "../../scripts/persist-tmp.mjs";
|
|
23
23
|
import { parseNeed } from "../../scripts/parse-need.mjs";
|
|
24
24
|
import { resolveJobCwd, runJob } from "../../scripts/grunt-job.mjs";
|
|
25
|
+
import { otherLines, recordHook } from "../../scripts/board.mjs";
|
|
25
26
|
|
|
26
27
|
export const ORCHESTRATOR_LOGS_DIR = ".tmp/grunt/orchestrator-logs";
|
|
27
28
|
/** One-release dual-read; drop next release. */
|
|
@@ -329,7 +330,14 @@ function userPromptSubmit(data) {
|
|
|
329
330
|
if (isHostStopBanner(prompt)) return 0;
|
|
330
331
|
unlinkStamp(data, "stop-block");
|
|
331
332
|
applySessionGateSlash(data, prompt);
|
|
332
|
-
|
|
333
|
+
let ctx = effectiveGruntContext(data);
|
|
334
|
+
try {
|
|
335
|
+
recordHook(data, process.env);
|
|
336
|
+
const others = otherLines(data, process.env);
|
|
337
|
+
if (others) ctx = ctx ? `${ctx}\n${others}` : others;
|
|
338
|
+
} catch {
|
|
339
|
+
// Board is best-effort. The session receipt still ships.
|
|
340
|
+
}
|
|
333
341
|
if (!ctx) return 0;
|
|
334
342
|
emit({
|
|
335
343
|
hookSpecificOutput: {
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: jev
|
|
3
|
+
description: >-
|
|
4
|
+
Yes/no or named-option decision on text already saved. Run node
|
|
5
|
+
scripts/jev.mjs --available first. On yes, one decision call. On no, decide
|
|
6
|
+
yourself and do not mention Jev.
|
|
7
|
+
---
|
|
8
|
+
# jev
|
|
9
|
+
|
|
10
|
+
Use this when the source text is already saved and the leftover question is a yes/no or a choice among options the research already named. Spec: `.rulesync/reference/jev.md`.
|
|
11
|
+
|
|
12
|
+
Run `node scripts/jev.mjs --available`. It prints `yes` or `no` and exits 0.
|
|
13
|
+
|
|
14
|
+
`no`: answer the question yourself. Do not run a decision call. Do not mention Jev, a missing key, or setup.
|
|
15
|
+
|
|
16
|
+
`yes`: read `.rulesync/reference/jev.md`. Write the passage and one questions file under `.tmp/grunt/jev/`. Run `node scripts/jev.mjs --state <state> --questions <questions>` once. Use the printed label and confidence. A low confidence is the result. Do not call again.
|
|
17
|
+
|
|
18
|
+
Open research, code, and grunt-job stay with the session. If the decision call exits non-zero, answer from the sources. Do not invent a label.
|
|
@@ -6,7 +6,7 @@ Protocol stays overview; domain fills this.
|
|
|
6
6
|
|
|
7
7
|
## Skills naming
|
|
8
8
|
|
|
9
|
-
Reserved (do not reuse in consumer custom skills): `ask` `auto` `browser` `clasp` `commit` `commit-and-push` `commit-push` `commit-push-deploy` `commit-push-release` `explain` `google-workspace` `handoff` `implement-plan` `listen` `pickup` `speak` `su` `tmp` `write-plan`.
|
|
9
|
+
Reserved (do not reuse in consumer custom skills): `ask` `auto` `browser` `clasp` `commit` `commit-and-push` `commit-push` `commit-push-deploy` `commit-push-release` `explain` `google-workspace` `handoff` `implement-plan` `jev` `listen` `pickup` `speak` `su` `tmp` `write-plan`.
|
|
10
10
|
|
|
11
11
|
Override: same name → one SSOT path (`.rulesync/skills/<name>/`). Re-init / `grunt upgrade` force-refresh overwrites grunt-owned names; consumer extras kept. Maps `origin` badge ≠ content picker.
|
|
12
12
|
|
|
@@ -29,6 +29,7 @@ Upgrade copies the current package trees and product scripts, then deletes cumul
|
|
|
29
29
|
| google-workspace | local | Google Sheets, Docs, Slides, Calendar, Gmail via node scripts/google-workspace.mjs. | | | |
|
|
30
30
|
| handoff | local | Write a session handoff under .tmp/grunt/handoffs/. Continue with /pickup. | | | |
|
|
31
31
|
| implement-plan | local | Execute remaining leaves in a .tmp/grunt/plans checklist. Skip [x]. | | | |
|
|
32
|
+
| jev | local | Yes/no or named-option decision on text already saved. Run node scripts/jev.mjs --available first. On yes, one decision… | | | |
|
|
32
33
|
| listen | local | Speech-to-text via node scripts/listen.mjs (ffmpeg mic + local whisper.cpp, OpenAI fallback). Input only. | | | |
|
|
33
34
|
| pickup | local | Continue a handoff under .tmp/grunt/handoffs/. | | | |
|
|
34
35
|
| speak | local | Text-to-speech via node scripts/speak.mjs (ElevenLabs or OpenAI). Output only. | | | |
|
|
@@ -46,6 +47,7 @@ Upgrade copies the current package trees and product scripts, then deletes cumul
|
|
|
46
47
|
| .rulesync/reference/google-workspace.md | google-workspace | google-workspace | Create and edit Google Sheets, Docs, Slides, Calendar events, and Gmail from the session. |
|
|
47
48
|
| .rulesync/reference/hooks.md | Hooks | hooks | SessionStart carries only the folder map: Claude / Codex run `../../scripts/session-map.mjs`, which injects the code-on… |
|
|
48
49
|
| .rulesync/reference/implementation-format.md | Implementation format (SSOT) | | Local session journal for `/implement-plan`. Not a plan, handoff, or git commit. |
|
|
50
|
+
| .rulesync/reference/jev.md | Jev | jev | Closed external decision. The source text is already saved. The question is a yes/no or a named choice. One call. Not o… |
|
|
49
51
|
| .rulesync/reference/law.md | Law | | Protocol stays overview; domain fills this. |
|
|
50
52
|
| .rulesync/reference/listen.md | Listen | listen | Speech-to-text from the session. Input only. Not TTS. Not a live voice agent. Not MCP. |
|
|
51
53
|
| .rulesync/reference/map.md | Map | map | Cheap outline. Not a file dump. |
|
|
@@ -6,7 +6,7 @@ tags: [hooks]
|
|
|
6
6
|
|
|
7
7
|
SessionStart carries only the folder map: Claude / Codex run `../../scripts/session-map.mjs`, which injects the code-only map from `../../scripts/folder-map.mjs` as `additionalContext` (capped under Claude Code's 10,000-char per-hook `additionalContext` limit, about 2.4k tokens, below the 5k `grunt map` budget; cache-stable, fail-open) and logs `{rows, tokens}` to `.tmp/grunt/sessions/<sid>/map.json`. Inject nothing else at SessionStart. Antigravity has no rulesync sessionStart. Do not register a SessionStart hook on `../../.grok/hooks/orchestrate-parent.json`.
|
|
8
8
|
|
|
9
|
-
Grok PostToolUse / SessionStart are observe-only — do not attempt output scrub; compress via RTK / spawn / isolation facts only. Do not add PostToolUse scrub hooks.
|
|
9
|
+
Grok PostToolUse / SessionStart are observe-only — do not attempt output scrub; compress via RTK / spawn / isolation facts only. Do not add PostToolUse scrub hooks. UserPromptSubmit may emit a one-line `sessionGate=auto|ask` plus session receipt, then up to 8 other live lines from `~/.grunt/board/` (`hookSpecificOutput.additionalContext`). The current session's own line is omitted. Skip host Stop banners. `/ask` exact-match slash stamps `session-gate-{sid}` to `ask`. `/auto` unlinks. Sid-less: no stamp. Default: `auto`.
|
|
10
10
|
|
|
11
11
|
In `sessionGate=ask`, parent Stop on `end_turn` blocks unless the recap asks (`?`) or is exactly `[orchestrator]: wait grunt`. Reason: `ask after this step`. Cap 3. `sessionGate=auto` does not Stop-block.
|
|
12
12
|
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
---
|
|
2
|
+
tags: [jev]
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Jev
|
|
6
|
+
|
|
7
|
+
Closed external decision. The source text is already saved. The question is a yes/no or a named choice. One call. Not open research. Not code. Not grunt-job.
|
|
8
|
+
|
|
9
|
+
## Availability
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
node scripts/jev.mjs --available
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Prints `yes` or `no` and exits 0. No request. No setup line.
|
|
16
|
+
|
|
17
|
+
`no`: the session answers the question itself. Do not run the decision command. Do not mention Jev, a missing key, or setup.
|
|
18
|
+
|
|
19
|
+
`yes`: continue below.
|
|
20
|
+
|
|
21
|
+
A key is `TYPESAFE_API_KEY` or `apiKey` in `~/.grunt/jev.json` (mode 0600). Env wins. Setup, when a person asks for it: `node scripts/setup.mjs jev`. Spec: `.rulesync/reference/setup.md`.
|
|
22
|
+
|
|
23
|
+
## Decision
|
|
24
|
+
|
|
25
|
+
Write `.tmp/grunt/jev/<slug>-state.txt` (plain text or a JSON object) and `.tmp/grunt/jev/<slug>-questions.json` (the System One questions map). Put every label for that study in the one map. At most 8 questions. State at most 64000 characters.
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
node scripts/jev.mjs --state .tmp/grunt/jev/<slug>-state.txt --questions .tmp/grunt/jev/<slug>-questions.json
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Yes/no:
|
|
32
|
+
|
|
33
|
+
```json
|
|
34
|
+
{ "keep": { "type": "noul", "instructions": "Does this passage promise zero retention on the default plan?" } }
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Named choice:
|
|
38
|
+
|
|
39
|
+
```json
|
|
40
|
+
{
|
|
41
|
+
"endpoint": {
|
|
42
|
+
"type": "choice",
|
|
43
|
+
"instructions": "Which endpoint should the app call?",
|
|
44
|
+
"criteria": {
|
|
45
|
+
"typesafe": "Vendor host, path, and key",
|
|
46
|
+
"other": "A different host, path, or key"
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Model is `jev-1.13.0`. Stdout is at most 8 lines: model, input tokens, then one label line. Exit 0. Use that label and confidence. A low confidence is the result. Do not rephrase and call again.
|
|
53
|
+
|
|
54
|
+
One request per process. UTC-day ledger `~/.grunt/jev-usage.json` stops at 40 requests or 1000000 input tokens. A 429 or 529 retries once only when `Retry-After` is at most 2 seconds.
|
|
55
|
+
|
|
56
|
+
Exit 2 means the decision call did not return a label. Answer from the sources. Do not invent a label. Do not print the key or the state.
|
|
@@ -4,7 +4,7 @@ Protocol stays overview; domain fills this.
|
|
|
4
4
|
|
|
5
5
|
## Skills naming
|
|
6
6
|
|
|
7
|
-
Reserved (do not reuse in consumer custom skills): `ask` `auto` `browser` `clasp` `commit` `commit-and-push` `commit-push` `commit-push-deploy` `commit-push-release` `explain` `google-workspace` `handoff` `implement-plan` `listen` `pickup` `speak` `su` `tmp` `write-plan`.
|
|
7
|
+
Reserved (do not reuse in consumer custom skills): `ask` `auto` `browser` `clasp` `commit` `commit-and-push` `commit-push` `commit-push-deploy` `commit-push-release` `explain` `google-workspace` `handoff` `implement-plan` `jev` `listen` `pickup` `speak` `su` `tmp` `write-plan`.
|
|
8
8
|
|
|
9
9
|
Override: same name → one SSOT path (`.rulesync/skills/<name>/`). Re-init / `grunt upgrade` force-refresh overwrites grunt-owned names; consumer extras kept. Maps `origin` badge ≠ content picker.
|
|
10
10
|
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
| .rulesync/reference/google-workspace.md | google-workspace | google-workspace | Create and edit Google Sheets, Docs, Slides, Calendar events, and Gmail from the session. |
|
|
11
11
|
| .rulesync/reference/hooks.md | Hooks | hooks | SessionStart carries only the folder map: Claude / Codex run `../../scripts/session-map.mjs`, which injects the code-on… |
|
|
12
12
|
| .rulesync/reference/implementation-format.md | Implementation format (SSOT) | | Local session journal for `/implement-plan`. Not a plan, handoff, or git commit. |
|
|
13
|
+
| .rulesync/reference/jev.md | Jev | jev | Closed external decision. The source text is already saved. The question is a yes/no or a named choice. One call. Not o… |
|
|
13
14
|
| .rulesync/reference/law.md | Law | | Protocol stays overview; domain fills this. |
|
|
14
15
|
| .rulesync/reference/listen.md | Listen | listen | Speech-to-text from the session. Input only. Not TTS. Not a live voice agent. Not MCP. |
|
|
15
16
|
| .rulesync/reference/map.md | Map | map | Cheap outline. Not a file dump. |
|
|
@@ -13,9 +13,10 @@ node scripts/setup.mjs speak
|
|
|
13
13
|
node scripts/setup.mjs listen
|
|
14
14
|
node scripts/setup.mjs google-workspace
|
|
15
15
|
node scripts/setup.mjs browser
|
|
16
|
+
node scripts/setup.mjs jev
|
|
16
17
|
```
|
|
17
18
|
|
|
18
|
-
TTY: menu with ok/missing. Already ok asks redo (`--redo` skips the ask). Missing bins: print the OS command (never run it), then re-check PATH. Ends with
|
|
19
|
+
TTY: menu with ok/missing. The menu lists enabled packs and always lists browser and jev. A pack is enabled when `.rulesync/grunt.features.jsonc` names it. `node scripts/setup.mjs <pack>` when that pack is off prints how to enable it (`grunt init` or `grunt upgrade`) and exits 1. Already ok asks redo (`--redo` skips the ask). Missing bins: print the OS command (never run it), then re-check PATH. Ends with one status line per listed target. Non-TTY needs flags (keys / `--creds`). Doctor never installs bins; setup never installs bins either.
|
|
19
20
|
|
|
20
21
|
| target | what it writes | flags |
|
|
21
22
|
| --- | --- | --- |
|
|
@@ -23,5 +24,6 @@ TTY: menu with ok/missing. Already ok asks redo (`--redo` skips the ask). Missin
|
|
|
23
24
|
| listen | same file (`openai` / `listen`) plus `~/.grunt/whisper/` | `--openai-key` `--device` `--skip-download` |
|
|
24
25
|
| google-workspace | Desktop OAuth JSON + tokens under `~/.grunt/` | `--creds PATH` `--account NAME` `--project ID` |
|
|
25
26
|
| browser | nothing (print Lightpanda / Chromium hints; both required except win32 Chromium-only) | |
|
|
27
|
+
| jev | `~/.grunt/jev.json` (`apiKey`, mode 0600). `TYPESAFE_API_KEY` wins when set | `--api-key` |
|
|
26
28
|
|
|
27
29
|
Do not invent keys. If the session is not a TTY, tell the user to run `grunt setup` in their terminal. This spec is enough. Do not read https://github.com/lovrozagar/grunt#readme unless this file is missing.
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
| google-workspace | local | Google Sheets, Docs, Slides, Calendar, Gmail via node scripts/google-workspace.mjs. | | | |
|
|
18
18
|
| handoff | local | Write a session handoff under .tmp/grunt/handoffs/. Continue with /pickup. | | | |
|
|
19
19
|
| implement-plan | local | Execute remaining leaves in a .tmp/grunt/plans checklist. Skip [x]. | | | |
|
|
20
|
+
| jev | local | Yes/no or named-option decision on text already saved. Run node scripts/jev.mjs --available first. On yes, one decision… | | | |
|
|
20
21
|
| listen | local | Speech-to-text via node scripts/listen.mjs (ffmpeg mic + local whisper.cpp, OpenAI fallback). Input only. | | | |
|
|
21
22
|
| pickup | local | Continue a handoff under .tmp/grunt/handoffs/. | | | |
|
|
22
23
|
| speak | local | Text-to-speech via node scripts/speak.mjs (ElevenLabs or OpenAI). Output only. | | | |
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: jev
|
|
3
|
+
description: "Yes/no or named-option decision on text already saved. Run node scripts/jev.mjs --available first. On yes, one decision call. On no, decide yourself and do not mention Jev."
|
|
4
|
+
---
|
|
5
|
+
# jev
|
|
6
|
+
|
|
7
|
+
Use this when the source text is already saved and the leftover question is a yes/no or a choice among options the research already named. Spec: `.rulesync/reference/jev.md`.
|
|
8
|
+
|
|
9
|
+
Run `node scripts/jev.mjs --available`. It prints `yes` or `no` and exits 0.
|
|
10
|
+
|
|
11
|
+
`no`: answer the question yourself. Do not run a decision call. Do not mention Jev, a missing key, or setup.
|
|
12
|
+
|
|
13
|
+
`yes`: read `.rulesync/reference/jev.md`. Write the passage and one questions file under `.tmp/grunt/jev/`. Run `node scripts/jev.mjs --state <state> --questions <questions>` once. Use the printed label and confidence. A low confidence is the result. Do not call again.
|
|
14
|
+
|
|
15
|
+
Open research, code, and grunt-job stay with the session. If the decision call exits non-zero, answer from the sources. Do not invent a label.
|
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- `scripts/jev.mjs`: one TypeSafe System One call for an external research decision, with a per-process and UTC-day cap. Key via `node scripts/setup.mjs jev` (`~/.grunt/jev.json`) or `TYPESAFE_API_KEY`. Doctor reports present or absent. `--available` prints `yes` or `no` and exits 0
|
|
13
|
+
- `/jev` skill: a yes/no or named-option decision uses Jev only when `--available` prints `yes`. A missing key is answered by the session, with no setup error
|
|
14
|
+
- Live agent board at `~/.grunt/board/`: one line per running agent (time, provider, model, effort, session, directory, work). A read drops a dead process. SessionStart `additionalContext` stays the folder map. UserPromptSubmit can append up to 8 other live lines.
|
|
15
|
+
- `google-workspace`, `listen`, `speak`, and `clasp` are opt-in. Init and upgrade ask with a multiselect that starts clear. `--packs` selects them without a TTY (`none` selects nothing). A repo with no `.rulesync/grunt.features.jsonc` drops those copies on the next init or upgrade. The npm package still ships them. Browser stays installed. Setup lists enabled packs and always lists browser and jev. Doctor always reports jev, and reports the other four only when that pack is enabled.
|
|
16
|
+
|
|
10
17
|
### Fixed
|
|
11
18
|
|
|
12
19
|
- `grunt upgrade` keeps extra `##` sections in shared reference docs such as `law.md`, so consumer INDEX content survives the copy
|
package/README.md
CHANGED
|
@@ -64,6 +64,7 @@ Exit 1 if any required missing; 0 if all required ok. Optional tools are reporte
|
|
|
64
64
|
| ffmpeg | no | mic capture for `/listen` · mac `brew install ffmpeg` · linux `sudo apt install ffmpeg` · win `winget install Gyan.FFmpeg` |
|
|
65
65
|
| whisper-cli | no | local STT for `/listen` · mac `brew install whisper-cpp` · first listen downloads `ggml-base.en.bin` · setup in `.rulesync/reference/listen.md` |
|
|
66
66
|
| listen | no | `node scripts/listen.mjs` · STT input · `grunt setup` / `node scripts/setup.mjs listen` · local whisper.cpp then OpenAI Whisper |
|
|
67
|
+
| jev | no | `node scripts/jev.mjs --state FILE --questions FILE` · one closed external research decision · `grunt setup` / `node scripts/setup.mjs jev` · `TYPESAFE_API_KEY` or `~/.grunt/jev.json` · local cap, one request per process |
|
|
67
68
|
|
|
68
69
|
Rulesync schema doctor is separate: `grunt:rulesync:doctor` (or SoT `rulesync:doctor`).
|
|
69
70
|
|
|
@@ -83,7 +84,7 @@ Rulesync schema doctor is separate: `grunt:rulesync:doctor` (or SoT `rulesync:do
|
|
|
83
84
|
- `sync-globals` → run `grunt:sync:globals` (dry-run); `--apply` → `grunt:sync:globals:apply`
|
|
84
85
|
- `purge-mcps` → run `grunt:purge:global-mcps` (dry-run); `--apply` → `grunt:purge:global-mcps:apply`
|
|
85
86
|
- `doctor` → run `grunt:doctor` (`grunt doctor` / `node scripts/doctor.mjs` stay). Rulesync schema: `grunt:rulesync:doctor`
|
|
86
|
-
- `setup` → run `grunt:setup` (`node scripts/setup.mjs`)
|
|
87
|
+
- `setup` → run `grunt:setup` (`node scripts/setup.mjs`). The menu lists enabled packs and always lists browser and jev. Flags for non-TTY keys/`--creds`
|
|
87
88
|
- `upgrade` → install the latest grunt as a devDependency and re-run with it, then the same merge as init for an already-inited repo: copy owned trees/scripts, prune retired grunt-owned names, print reserved skill names
|
|
88
89
|
- `map [dir]` → code-only folder map (`scripts/folder-map.mjs`); no package manager needed. See [Folder map](#folder-map)
|
|
89
90
|
- `help`
|
|
@@ -97,6 +98,7 @@ Rulesync schema doctor is separate: `grunt:rulesync:doctor` (or SoT `rulesync:do
|
|
|
97
98
|
- `--apply` — write for `sync-globals` / `purge-mcps`
|
|
98
99
|
- `--host <id>` — `sync-globals --host <id>`
|
|
99
100
|
- `--pm <name>` — `npm` | `yarn` | `pnpm` | `bun`
|
|
101
|
+
- `--packs <list>` — `google-workspace`, `listen`, `speak`, `clasp`, comma-separated, or `none`
|
|
100
102
|
|
|
101
103
|
## Init
|
|
102
104
|
|
|
@@ -110,6 +112,7 @@ Rulesync schema doctor is separate: `grunt:rulesync:doctor` (or SoT `rulesync:do
|
|
|
110
112
|
- First init (no sentinel) applies globals unless flagged
|
|
111
113
|
- Owned trees/scripts refresh; extra `.rulesync` files kept; patches to grunt-owned files lost
|
|
112
114
|
- `cp` cannot delete dest extras. Init/upgrade then prune: retired skills `parent` `solo` `cascade`, agents `implementer` `thinker` (plus `.grok/roles/{implementer,thinker}.toml` and `.gemini/agents/{name}/`), scripts `telemetry.mjs` `grunt-config.mjs`, paths `.grok/parent.md` `.grok/skills/shared` `.rulesync/grunt.config.jsonc` plus local overlay and example, and reserved skill dirs this package no longer ships. Consumer extras stay.
|
|
115
|
+
- Optional packs `google-workspace`, `listen`, `speak`, and `clasp` start off. A TTY init or upgrade asks with a multiselect that starts clear. A saved `.rulesync/grunt.features.jsonc` is pre-checked. `--packs` answers without a TTY (`none` selects nothing). No features file means those four are not copied, and the next init or upgrade removes copies an older grunt installed. The npm package still contains them. Browser and the other core skills stay. The four names stay reserved. The features file is written only after an answer.
|
|
113
116
|
- Breaking: consumer scripts are `grunt:<SoT-key>` (`grunt:rulesync:generate`, `grunt:doctor`). Re-init migrates `package.json` (owned unprefixed keys + suffixes; `npm|yarn|pnpm|bun run` refs in other dest scripts). CI/husky/`run rulesync:*` / `run doctor` must switch. No aliases. SoT repo scripts stay unprefixed (`rulesync:generate`).
|
|
114
117
|
|
|
115
118
|
## Version bump
|
|
@@ -125,7 +128,7 @@ bunx grunt upgrade
|
|
|
125
128
|
|
|
126
129
|
`upgrade` checks the registry (`npm_config_registry`, else npmjs) for the latest `@lovrozagar/grunt`. When it is newer than the running copy, `upgrade` adds it as a devDependency with the detected package manager (`-w` at a pnpm workspace root), then re-runs the same command with the installed bin so the merge uses the new package. Offline or a failed check prints one warning and upgrades with the running version. `--no-self-update` skips the check. The grunt repo itself and local builds newer than the registry never self-update.
|
|
127
130
|
|
|
128
|
-
`upgrade` is init plus a reserved-names print. Same merge, prune, and globals-skip rules as Init. New skills/scripts appear because they are in the package copy list. Dropped grunt-owned files disappear only if they are on the retired lists
|
|
131
|
+
`upgrade` is init plus a reserved-names print. Same merge, prune, and globals-skip rules as Init. New skills/scripts appear because they are in the package copy list. Optional packs are the exception: they are copied only when selected, and removed when they are not. Dropped grunt-owned files disappear only if they are on the retired lists, are an unselected optional pack, or are a reserved skill this package no longer ships. Do not expect a blind dest-dir mirror-delete; that would wipe consumer extras.
|
|
129
132
|
|
|
130
133
|
## Agents
|
|
131
134
|
|
|
@@ -148,7 +151,9 @@ Lightpanda-first session CLI: `node scripts/browser.mjs`. Zero MCP. Zero env kno
|
|
|
148
151
|
|
|
149
152
|
Present under `.claude` / `.rulesync` / `.agents` / `.grok` (`rulesync -f skills` mirrors SSOT):
|
|
150
153
|
|
|
151
|
-
- `ask` `auto` `browser` `clasp` `commit` `commit-and-push` (1-release alias → `commit-push`) `commit-push` `commit-push-deploy` `commit-push-release` `explain` `google-workspace` `handoff` `implement-plan` `listen` `pickup` `speak` `su` `tmp` `write-plan`
|
|
154
|
+
- `ask` `auto` `browser` `clasp` `commit` `commit-and-push` (1-release alias → `commit-push`) `commit-push` `commit-push-deploy` `commit-push-release` `explain` `google-workspace` `handoff` `implement-plan` `jev` `listen` `pickup` `speak` `su` `tmp` `write-plan`
|
|
155
|
+
|
|
156
|
+
`google-workspace`, `listen`, `speak`, and `clasp` are copied into a consumer only when that pack is selected. The names stay reserved.
|
|
152
157
|
|
|
153
158
|
`/auto` (default) keeps going and asks on blockers. `/ask` finishes one step, recaps, then asks. `/auto` returns the session to auto.
|
|
154
159
|
|
|
@@ -182,7 +187,7 @@ Emit writes other-CLI trees from `.rulesync` for the **next** process of that CL
|
|
|
182
187
|
|
|
183
188
|
### Folder map
|
|
184
189
|
|
|
185
|
-
Claude and Codex get a folder map at SessionStart (`scripts/session-map.mjs`), so agents skip `ls`/`find` loops. Agents run `node scripts/folder-map.mjs <dir>` (same as `grunt map <dir>`) for depth. Grok and Antigravity get the `AGENTS.md` line only. The injected map stays under Claude Code's 10,000-character per-hook `additionalContext` limit (about 2.4k tokens); past it, Claude keeps only a 2KB preview inline. `grunt map` itself uses the full `budget`.
|
|
190
|
+
Claude and Codex get a folder map at SessionStart (`scripts/session-map.mjs`), so agents skip `ls`/`find` loops. Agents run `node scripts/folder-map.mjs <dir>` (same as `grunt map <dir>`) for depth. Grok and Antigravity get the `AGENTS.md` line only. The injected map stays under Claude Code's 10,000-character per-hook `additionalContext` limit (about 2.4k tokens); past it, Claude keeps only a 2KB preview inline. `grunt map` itself uses the full `budget`. SessionStart `additionalContext` stays that map.
|
|
186
191
|
|
|
187
192
|
- Folders only, never files. Source: `git ls-files --cached --others --exclude-standard`, so gitignored paths never show
|
|
188
193
|
- A folder shows when it or a descendant holds a code file (`codeExt`) and no path segment is a dot dir or in `skip`
|
|
@@ -203,11 +208,17 @@ Optional `.rulesync/grunt.map.jsonc` (missing or invalid → defaults):
|
|
|
203
208
|
| `include` | `[]` | globs always shown (`*` one segment, `**` any) |
|
|
204
209
|
| `exclude` | `[]` | globs never shown |
|
|
205
210
|
|
|
211
|
+
### Agent board
|
|
212
|
+
|
|
213
|
+
One live line per running agent, stored in `~/.grunt/board/` (one file per session, this machine). The line is time, provider, model, effort, session id, directory, and current work. A missing model or effort is `-`. A read deletes a line whose process is gone, so quit, crash, and OOM drop off at the next read.
|
|
214
|
+
|
|
215
|
+
Claude and Codex write the row at SessionStart. `additionalContext` stays the folder map. Every UserPromptSubmit refreshes the row and may append up to 8 other live lines. Grok writes on the prompt. Antigravity runs `node scripts/board.mjs set --work` when the work changes and `node scripts/board.mjs` to read.
|
|
216
|
+
|
|
206
217
|
### Secrets (machine)
|
|
207
218
|
|
|
208
219
|
Not git. Not `sync-globals`.
|
|
209
220
|
|
|
210
|
-
First-hand: `grunt setup` (TTY). Per target: `node scripts/setup.mjs speak|listen|google-workspace|browser`. Spec: `.rulesync/reference/setup.md`.
|
|
221
|
+
First-hand: `grunt setup` (TTY). The menu lists enabled packs and always lists browser and jev. Per target: `node scripts/setup.mjs speak|listen|google-workspace|browser`. Spec: `.rulesync/reference/setup.md`.
|
|
211
222
|
|
|
212
223
|
| | where |
|
|
213
224
|
| --- | --- |
|
|
@@ -216,7 +227,7 @@ First-hand: `grunt setup` (TTY). Per target: `node scripts/setup.mjs speak|liste
|
|
|
216
227
|
| Listen extras | `WHISPER_MODEL` `LISTEN_DEVICE` `LISTEN_STT` `SPEAK_PROVIDER` — optional |
|
|
217
228
|
| Google Workspace | OAuth under `~/.grunt/` |
|
|
218
229
|
|
|
219
|
-
`~/.grunt/speak.json` chmod 600. Env wins over that file. Copy the file or export env on another machine. Doctor reports
|
|
230
|
+
`~/.grunt/speak.json` chmod 600. Env wins over that file. Copy the file or export env on another machine. Doctor always reports gh and jev. It reports google-workspace, speak, ffmpeg, and whisper-cli when that pack is enabled.
|
|
220
231
|
|
|
221
232
|
`sync-globals` is host CLI globals (MCP and friends), not API keys.
|
|
222
233
|
|
|
@@ -332,6 +343,7 @@ The session agent runs `node scripts/grunt-job.mjs --job search|exec|slice|fetch
|
|
|
332
343
|
| `scrub-spawn-prompt` / `parse-need` / `grunt-job` | yes | **local** | Hooks and scripts. Isolation facts are grunt-job output, not a model hop. |
|
|
333
344
|
| emit / generate / init | yes (this repo / install) | **local** | Writes other-CLI configs. Not a runtime line to those CLIs. |
|
|
334
345
|
| WebSearch / web_fetch | tool from host | **remote-not-LLM** | Network search/fetch. Not a second Model API box. Snippet/cite/"what is X"/world fact: grunt `job: web`. Live URL/DOM: grunt browser rail, not this box. |
|
|
346
|
+
| `jev` | yes | **remote-not-LLM** | `node scripts/jev.mjs --state FILE --questions FILE`. One System One decision about external source text. Not the host Model API. Not grunt-job. |
|
|
335
347
|
| MCP | policy deny | n/a | Denied by policy in-tree. Do not draw as a main path. |
|
|
336
348
|
| Model API | **no** (one box outside) | AI-server | This host’s vendor SDK only. Parent and child **turns** complete here. Spawn omits model; frontmatter on `.rulesync/subagents/*.md` picks the model. |
|
|
337
349
|
| Other CLIs | no (not this process) | emit/config only | Same protocol files emitted elsewhere. No hop, no shared peek. |
|
|
@@ -384,7 +396,7 @@ Repo-relative (repository root):
|
|
|
384
396
|
|
|
385
397
|
## Layout
|
|
386
398
|
|
|
387
|
-
Published (`package.json` `files`): `bin/grunt.js` `cli` `scripts/check-globals.mjs` `scripts/emit-agent-shell-tools.mjs` `scripts/emit-gemini.mjs` `scripts/emit-maps.mjs` `scripts/guarded-md.mjs` `scripts/guarded-roots.mjs` `scripts/emit-mcp-policy.mjs` `scripts/gate-fat-tools.mjs` `scripts/hooks-union.mjs` `scripts/pipeline.mjs` `scripts/grunt-job.mjs` `scripts/parse-need.mjs` `scripts/persist-handoff.mjs` `scripts/persist-implementation.mjs` `scripts/persist-tmp.mjs` `scripts/persist-plan.mjs` `scripts/purge-global-mcps.mjs` `scripts/scrub-spawn-prompt.mjs` `scripts/scrub-text-lib.mjs` `scripts/sync-global-settings.mjs` `scripts/browser.mjs` `scripts/speak.mjs` `scripts/listen.mjs` `scripts/google-workspace.mjs` `scripts/interactive.mjs` `scripts/prompt.mjs` `scripts/setup.mjs` `scripts/doctor.mjs` `scripts/skill-conflicts.mjs` `scripts/scrub-text` `.rulesync` `.grok` `.codex` `.claude` `.agents` `AGENTS.md` `CLAUDE.md` `.mcp.json` `README.md` `LICENSE` `CHANGELOG.md`
|
|
399
|
+
Published (`package.json` `files`): `bin/grunt.js` `cli` `scripts/check-globals.mjs` `scripts/emit-agent-shell-tools.mjs` `scripts/emit-gemini.mjs` `scripts/emit-maps.mjs` `scripts/guarded-md.mjs` `scripts/guarded-roots.mjs` `scripts/emit-mcp-policy.mjs` `scripts/gate-fat-tools.mjs` `scripts/hooks-union.mjs` `scripts/pipeline.mjs` `scripts/grunt-job.mjs` `scripts/parse-need.mjs` `scripts/persist-handoff.mjs` `scripts/persist-implementation.mjs` `scripts/persist-tmp.mjs` `scripts/persist-plan.mjs` `scripts/purge-global-mcps.mjs` `scripts/scrub-spawn-prompt.mjs` `scripts/scrub-text-lib.mjs` `scripts/sync-global-settings.mjs` `scripts/board.mjs` `scripts/browser.mjs` `scripts/feature-packs.mjs` `scripts/speak.mjs` `scripts/listen.mjs` `scripts/jev.mjs` `scripts/google-workspace.mjs` `scripts/interactive.mjs` `scripts/prompt.mjs` `scripts/setup.mjs` `scripts/doctor.mjs` `scripts/skill-conflicts.mjs` `scripts/scrub-text` `.rulesync` `.grok` `.codex` `.claude` `.agents` `AGENTS.md` `CLAUDE.md` `.mcp.json` `README.md` `LICENSE` `CHANGELOG.md`
|
|
388
400
|
|
|
389
401
|
No `scripts/*.test.ts` `scripts/fixtures/` `docs/` `coverage/` `vitest.config.ts` in `files`. `cli` dir ships whole (includes `cli/*.test.ts`).
|
|
390
402
|
|
package/cli/grunt.mjs
CHANGED
|
@@ -3,7 +3,7 @@ import { readFileSync } from "node:fs"
|
|
|
3
3
|
import path from "node:path"
|
|
4
4
|
import { fileURLToPath } from "node:url"
|
|
5
5
|
import { destAlreadyInited, init, RESERVED_SKILLS, toGruntScriptName } from "./init.mjs"
|
|
6
|
-
import { confirm, isInteractive, select, spinner } from "./prompt.mjs"
|
|
6
|
+
import { confirm, isInteractive, multiselect, select, spinner } from "./prompt.mjs"
|
|
7
7
|
import { selfUpdate } from "./self-update.mjs"
|
|
8
8
|
import {
|
|
9
9
|
PACKAGE_MANAGER_ASK,
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
runScriptArgs,
|
|
15
15
|
} from "../scripts/package-manager.mjs"
|
|
16
16
|
import { mapCommand } from "../scripts/folder-map.mjs"
|
|
17
|
+
import { PACK_OPTIONS, parsePacksFlag, selectedPacks } from "../scripts/feature-packs.mjs"
|
|
17
18
|
|
|
18
19
|
const PKG_ROOT = path.join(path.dirname(fileURLToPath(import.meta.url)), "..")
|
|
19
20
|
|
|
@@ -28,7 +29,7 @@ Commands:
|
|
|
28
29
|
sync-globals run grunt:sync:globals (dry-run; --apply to write)
|
|
29
30
|
purge-mcps run grunt:purge:global-mcps (dry-run; --apply to write)
|
|
30
31
|
doctor run grunt:doctor
|
|
31
|
-
setup run grunt:setup — handheld keys/OAuth (speak, listen, google-workspace, browser)
|
|
32
|
+
setup run grunt:setup — handheld keys/OAuth (speak, listen, google-workspace, browser, jev)
|
|
32
33
|
upgrade Self-update to the latest grunt, then re-init: copy owned files, prune retired names, print reserved skills
|
|
33
34
|
map [dir] Code-only folder map (git-tracked, no files); dir for depth
|
|
34
35
|
help Show this help
|
|
@@ -42,6 +43,7 @@ Flags:
|
|
|
42
43
|
--apply Write for sync-globals / purge-mcps
|
|
43
44
|
--host <id> sync-globals host
|
|
44
45
|
--pm <name> npm | yarn | pnpm | bun (else lockfile, then how grunt was launched, then ask)
|
|
46
|
+
--packs <list> Optional packs: google-workspace,listen,speak,clasp or none
|
|
45
47
|
`
|
|
46
48
|
|
|
47
49
|
const MENU_OPTIONS = [
|
|
@@ -94,6 +96,8 @@ export function parseArgv(argv) {
|
|
|
94
96
|
let hostError = false
|
|
95
97
|
let pm
|
|
96
98
|
let pmError = false
|
|
99
|
+
let packs
|
|
100
|
+
let packsError = false
|
|
97
101
|
const positionals = []
|
|
98
102
|
for (let i = 0; i < argv.length; i++) {
|
|
99
103
|
const a = argv[i]
|
|
@@ -141,6 +145,28 @@ export function parseArgv(argv) {
|
|
|
141
145
|
else pm = pm.toLowerCase()
|
|
142
146
|
continue
|
|
143
147
|
}
|
|
148
|
+
if (a === "--packs") {
|
|
149
|
+
const v = argv[i + 1]
|
|
150
|
+
if (v == null || String(v).startsWith("--")) {
|
|
151
|
+
packsError = true
|
|
152
|
+
continue
|
|
153
|
+
}
|
|
154
|
+
try {
|
|
155
|
+
packs = parsePacksFlag(v)
|
|
156
|
+
} catch {
|
|
157
|
+
packsError = true
|
|
158
|
+
}
|
|
159
|
+
i += 1
|
|
160
|
+
continue
|
|
161
|
+
}
|
|
162
|
+
if (typeof a === "string" && a.startsWith("--packs=")) {
|
|
163
|
+
try {
|
|
164
|
+
packs = parsePacksFlag(a.slice("--packs=".length))
|
|
165
|
+
} catch {
|
|
166
|
+
packsError = true
|
|
167
|
+
}
|
|
168
|
+
continue
|
|
169
|
+
}
|
|
144
170
|
positionals.push(a)
|
|
145
171
|
}
|
|
146
172
|
return {
|
|
@@ -153,6 +179,8 @@ export function parseArgv(argv) {
|
|
|
153
179
|
hostError,
|
|
154
180
|
pm,
|
|
155
181
|
pmError,
|
|
182
|
+
packs,
|
|
183
|
+
packsError,
|
|
156
184
|
}
|
|
157
185
|
}
|
|
158
186
|
|
|
@@ -184,9 +212,29 @@ export async function resolveCliPackageManager({ cwd, env, override, interactive
|
|
|
184
212
|
return null
|
|
185
213
|
}
|
|
186
214
|
|
|
187
|
-
async function
|
|
215
|
+
async function askPacks(cwd) {
|
|
216
|
+
return await multiselect({
|
|
217
|
+
message: "Optional packs",
|
|
218
|
+
options: PACK_OPTIONS,
|
|
219
|
+
initialValues: selectedPacks(cwd),
|
|
220
|
+
required: false,
|
|
221
|
+
})
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function initOpts(base, packs) {
|
|
225
|
+
if (packs === undefined) return base
|
|
226
|
+
return { ...base, packs }
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
async function runInit(cwd, { skipGlobals, interactive, packageManager, packs, packsOnly = false }) {
|
|
230
|
+
let selected = packs
|
|
231
|
+
if (packsOnly) {
|
|
232
|
+
if (selected === undefined && interactive) selected = await askPacks(cwd)
|
|
233
|
+
init(cwd, initOpts({ skipGlobals, packageManager }, selected))
|
|
234
|
+
return
|
|
235
|
+
}
|
|
188
236
|
if (!interactive) {
|
|
189
|
-
init(cwd, { skipGlobals, packageManager })
|
|
237
|
+
init(cwd, initOpts({ skipGlobals, packageManager }, selected))
|
|
190
238
|
return
|
|
191
239
|
}
|
|
192
240
|
if (destAlreadyInited(cwd)) {
|
|
@@ -196,16 +244,17 @@ async function runInit(cwd, { skipGlobals, interactive, packageManager }) {
|
|
|
196
244
|
})
|
|
197
245
|
if (!again) return
|
|
198
246
|
}
|
|
247
|
+
if (selected === undefined) selected = await askPacks(cwd)
|
|
199
248
|
const applyGlobals = await confirm({
|
|
200
249
|
message: APPLY_GLOBALS_CONFIRM,
|
|
201
250
|
initialValue: !skipGlobals,
|
|
202
251
|
})
|
|
203
|
-
init(cwd, {
|
|
252
|
+
init(cwd, initOpts({
|
|
204
253
|
skipGlobals: !applyGlobals,
|
|
205
254
|
applyGlobals,
|
|
206
255
|
onPhase: bindSpinner(),
|
|
207
256
|
packageManager,
|
|
208
|
-
})
|
|
257
|
+
}, selected))
|
|
209
258
|
}
|
|
210
259
|
|
|
211
260
|
async function dispatch(cmd, flags, interactive, pm) {
|
|
@@ -222,6 +271,7 @@ async function dispatch(cmd, flags, interactive, pm) {
|
|
|
222
271
|
skipGlobals: flags.skipGlobals,
|
|
223
272
|
interactive: interactive && (!cmd || cmd === "init"),
|
|
224
273
|
packageManager: pm,
|
|
274
|
+
packs: flags.packs,
|
|
225
275
|
})
|
|
226
276
|
return
|
|
227
277
|
}
|
|
@@ -270,8 +320,10 @@ async function dispatch(cmd, flags, interactive, pm) {
|
|
|
270
320
|
}
|
|
271
321
|
await runInit(process.cwd(), {
|
|
272
322
|
skipGlobals: flags.skipGlobals,
|
|
273
|
-
interactive
|
|
323
|
+
interactive,
|
|
324
|
+
packsOnly: true,
|
|
274
325
|
packageManager: pm,
|
|
326
|
+
packs: flags.packs,
|
|
275
327
|
})
|
|
276
328
|
process.stdout.write(`reserved: ${RESERVED_SKILLS.join(" ")}\n`)
|
|
277
329
|
return
|
|
@@ -286,7 +338,7 @@ function commandNeedsPackageManager(cmd) {
|
|
|
286
338
|
|
|
287
339
|
export async function start() {
|
|
288
340
|
const flags = parseArgv(process.argv.slice(2))
|
|
289
|
-
if (flags.hostError || flags.pmError) {
|
|
341
|
+
if (flags.hostError || flags.pmError || flags.packsError) {
|
|
290
342
|
process.stdout.write(USAGE)
|
|
291
343
|
process.exitCode = 1
|
|
292
344
|
return
|